@mongodb-js/mcp-http-runners 3.0.0-prerelease.2 → 3.0.0-prerelease.4

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/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export type { StreamableHttpRunnerOptions } from "./streamableHttpRunner.js";
2
2
  export { StreamableHttpRunner } from "./streamableHttpRunner.js";
3
3
  export { MCPHttpServer, type MCPHttpServerOptions } from "./mcpHttpServer.js";
4
+ export { LegacyMcpHttpHandler, type LegacyMcpHttpHandlerOptions, type LegacySessionOptions, } from "./legacyMcpHttpHandler.js";
5
+ export { SessionStore, SessionLimitExceededError, SessionRejectedError, createDefaultSessionStore, type ISessionStore, type SessionStoreConstructorArgs, type CreateSessionStoreFn, } from "@mongodb-js/mcp-core";
4
6
  export { MonitoringServer, type MonitoringServerOptions } from "./monitoringServer.js";
5
7
  export { ExpressBasedHttpServer, type ExpressBasedHttpServerOptions } from "./expressBasedHttpServer.js";
6
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +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"}
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,EACH,oBAAoB,EACpB,KAAK,2BAA2B,EAChC,KAAK,oBAAoB,GAC5B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACH,YAAY,EACZ,yBAAyB,EACzB,oBAAoB,EACpB,yBAAyB,EACzB,KAAK,aAAa,EAClB,KAAK,2BAA2B,EAChC,KAAK,oBAAoB,GAC5B,MAAM,sBAAsB,CAAC;AAC9B,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 CHANGED
@@ -2,6 +2,9 @@
2
2
  export { StreamableHttpRunner } from "./streamableHttpRunner.js";
3
3
  // HTTP Servers
4
4
  export { MCPHttpServer } from "./mcpHttpServer.js";
5
+ export { LegacyMcpHttpHandler, } from "./legacyMcpHttpHandler.js";
6
+ // Session lifecycle primitives live in @mongodb-js/mcp-core; re-export for convenience.
7
+ export { SessionStore, SessionLimitExceededError, SessionRejectedError, createDefaultSessionStore, } from "@mongodb-js/mcp-core";
5
8
  export { MonitoringServer } from "./monitoringServer.js";
6
9
  // Express HTTP Server base
7
10
  export { ExpressBasedHttpServer } from "./expressBasedHttpServer.js";
package/dist/index.js.map CHANGED
@@ -1 +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"}
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,EACH,oBAAoB,GAGvB,MAAM,2BAA2B,CAAC;AACnC,wFAAwF;AACxF,OAAO,EACH,YAAY,EACZ,yBAAyB,EACzB,oBAAoB,EACpB,yBAAyB,GAI5B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,gBAAgB,EAAgC,MAAM,uBAAuB,CAAC;AAEvF,2BAA2B;AAC3B,OAAO,EAAE,sBAAsB,EAAsC,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1,124 @@
1
+ import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node";
2
+ import type express from "express";
3
+ import type { ILogger, TransportRequestContext, HttpServerOptions, BaseServer } from "@mongodb-js/mcp-types";
4
+ import { type ISessionStore } from "@mongodb-js/mcp-core";
5
+ /** Builds a fresh request-scoped server for a legacy request. */
6
+ export type LegacyServerFactory = (request: TransportRequestContext) => Promise<BaseServer>;
7
+ /** Tunables forwarded to the {@link SessionStore}. */
8
+ export type LegacySessionOptions = {
9
+ maxSessions?: number;
10
+ idleTimeoutMS?: number;
11
+ notificationTimeoutMS?: number;
12
+ evictionIdleGraceMS?: number;
13
+ /**
14
+ * When true, the legacy (2025-era) HTTP transport accepts a client-supplied
15
+ * `mcp-session-id` header on `initialize` instead of rejecting it, and
16
+ * reuses that id for the session. When no session is found for a supplied
17
+ * id, the handler attempts an implicit re-initialization and restores the
18
+ * client's previously negotiated capabilities from the session store (see
19
+ * {@link ISessionStore.saveNegotiatedClientState}); a store without durable
20
+ * storage restores nothing. Defaults to `false`.
21
+ */
22
+ externallyManagedSessions?: boolean;
23
+ };
24
+ export type LegacyMcpHttpHandlerOptions = {
25
+ /** Builds a fresh request-scoped server for the legacy session. */
26
+ createServer: LegacyServerFactory;
27
+ logger: ILogger;
28
+ http: HttpServerOptions;
29
+ /** Session store backing the legacy sessionful path (auth-aware or durable). */
30
+ sessionStore: ISessionStore<NodeStreamableHTTPServerTransport>;
31
+ /**
32
+ * When true, accept a client-supplied `mcp-session-id` on `initialize` and
33
+ * restore negotiated client state for implicitly re-initialized sessions.
34
+ * Defaults to `false`.
35
+ */
36
+ externallyManagedSessions?: boolean;
37
+ };
38
+ /**
39
+ * The contract {@link MCPHttpServer.createLegacyHandler} returns for 2025-era
40
+ * serving. It is deliberately transport-shaped so alternative implementations
41
+ * (a different transport, a distributed session store, ...) can be returned by
42
+ * overriding `createLegacyHandler`.
43
+ */
44
+ export interface LegacyMcpHandler {
45
+ /** Serves one 2025-era HTTP request (POST/GET/DELETE) to `/mcp`. */
46
+ handle(req: express.Request, res: express.Response): Promise<void>;
47
+ /** Releases any held resources (e.g. live sessions) on shutdown. */
48
+ close(): Promise<void>;
49
+ }
50
+ /**
51
+ * Serves 2025-era (legacy) MCP requests over streamable HTTP **sessionfully**.
52
+ *
53
+ * The 2026-07-28 protocol is stateless and carries client identity per request
54
+ * in the `_meta` envelope. The 2025 protocol is not: it declares the client's
55
+ * negotiated capabilities (incl. `elicitation`) in the `initialize` handshake
56
+ * and delivers elicitation as real server→client requests. Both need a live,
57
+ * correlated transport, which is exactly what the SDK's legacy elicitation
58
+ * shim relies on. The SDK's `legacy: 'stateless'` mode cannot do that (each
59
+ * request gets a fresh instance: no capabilities, no return channel).
60
+ *
61
+ * This handler owns that sessionful lifecycle: an `initialize` POST creates a
62
+ * session (a connected transport/server pair), later requests and the SSE idle
63
+ * stream reuse the session's transport (the shim's return channel), and a
64
+ * DELETE closes it. Sessions are bounded by the injected `sessionStore`.
65
+ * Keeping it isolated from {@link MCPHttpServer} leaves the modern stateless
66
+ * path clean.
67
+ */
68
+ export declare class LegacyMcpHttpHandler implements LegacyMcpHandler {
69
+ private readonly createServer;
70
+ private readonly logger;
71
+ private readonly http;
72
+ private readonly sessions;
73
+ private readonly externallyManagedSessions;
74
+ /** Serializes concurrent initialization of the same session id (see {@link createSession}). */
75
+ private readonly pendingInitializations;
76
+ constructor({ createServer, logger, http, sessionStore, externallyManagedSessions, }: LegacyMcpHttpHandlerOptions);
77
+ /** Closes every live session (e.g. on server shutdown). */
78
+ close(): Promise<void>;
79
+ private buildTransportContext;
80
+ /** Reports a session error the way main did: same codes, messages, and statuses. */
81
+ private reportSessionError;
82
+ /**
83
+ * Keeps the session's SSE idle stream alive through proxies and reaps dead
84
+ * clients: pings every 30s and closes the transport after 3 consecutive
85
+ * failures. Not started in JSON response mode, where connections are
86
+ * short-lived and pings aren't needed.
87
+ */
88
+ private startKeepAliveLoop;
89
+ /**
90
+ * Creates a fresh session (or returns the existing / in-flight one for an
91
+ * externally managed id). Concurrent initializations of the same session id
92
+ * are serialized so they don't race on `addSession` (see
93
+ * {@link createSessionInstance}).
94
+ */
95
+ private createSession;
96
+ /** Builds the server/transport pair and registers it in the session store. */
97
+ private createSessionInstance;
98
+ /**
99
+ * Serves a 2025-era request. POSTs that carry no session begin a new session
100
+ * (the `initialize` handshake); POSTs/GETs/DELETEs that carry a session id
101
+ * are routed to the stored transport, preserving the client's negotiated
102
+ * state and providing the SSE return channel for server→client requests.
103
+ */
104
+ handle(req: express.Request, res: express.Response): Promise<void>;
105
+ /** Routes a session-carrying request to its live transport; reports a session error when missing. */
106
+ private handleSessionRequest;
107
+ /**
108
+ * Restores the client state negotiated during the session's original
109
+ * `initialize` onto an implicitly re-initialized server. The session's
110
+ * server on this pod never saw the client's `initialize` request, so
111
+ * without this it would treat the client as capability-less — e.g.
112
+ * skipping confirmation elicitation for destructive tools. No-op when the
113
+ * session store does not persist negotiated client state.
114
+ */
115
+ private restoreNegotiatedClientState;
116
+ /**
117
+ * Arranges for the client state negotiated by a real `initialize` exchange
118
+ * to be persisted through the session store, so implicit re-initializations
119
+ * of this session can restore it. Wraps the `oninitialized` callback
120
+ * installed by `server.connect`, hence must run after it.
121
+ */
122
+ private captureNegotiatedClientStateOnInitialize;
123
+ }
124
+ //# sourceMappingURL=legacyMcpHttpHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacyMcpHttpHandler.d.ts","sourceRoot":"","sources":["../src/legacyMcpHttpHandler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iCAAiC,EAAE,MAAM,4BAA4B,CAAC;AAC/E,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,EACR,OAAO,EACP,uBAAuB,EAEvB,iBAAiB,EACjB,UAAU,EAEb,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAA6B,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AA2BrF,iEAAiE;AACjE,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,EAAE,uBAAuB,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;AAE5F,sDAAsD;AACtD,MAAM,MAAM,oBAAoB,GAAG;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;;;;OAQG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACtC,mEAAmE;IACnE,YAAY,EAAE,mBAAmB,CAAC;IAClC,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,iBAAiB,CAAC;IACxB,gFAAgF;IAChF,YAAY,EAAE,aAAa,CAAC,iCAAiC,CAAC,CAAC;IAC/D;;;;OAIG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACvC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC7B,oEAAoE;IACpE,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,oEAAoE;IACpE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,oBAAqB,YAAW,gBAAgB;IACzD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IACnD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAU;IACjC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoB;IACzC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmD;IAC5E,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAU;IACpD,+FAA+F;IAC/F,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAiE;gBAE5F,EACR,YAAY,EACZ,MAAM,EACN,IAAI,EACJ,YAAY,EACZ,yBAAiC,GACpC,EAAE,2BAA2B;IAQ9B,2DAA2D;IAC9C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAInC,OAAO,CAAC,qBAAqB;IAc7B,oFAAoF;IACpF,OAAO,CAAC,kBAAkB;IAsC1B;;;;;OAKG;YACW,kBAAkB;IAgDhC;;;;;OAKG;YACW,aAAa;IAyC3B,8EAA8E;YAChE,qBAAqB;IA0FnC;;;;;OAKG;IACU,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IA8E/E,qGAAqG;YACvF,oBAAoB;IA0ClC;;;;;;;OAOG;YACW,4BAA4B;IAkC1C;;;;;OAKG;IACH,OAAO,CAAC,wCAAwC;CAwBnD"}
@@ -0,0 +1,437 @@
1
+ import { isInitializeRequest } from "@modelcontextprotocol/server";
2
+ import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node";
3
+ import { LogId, getRandomUUID, requestIdAttr } from "@mongodb-js/mcp-core";
4
+ import { SessionLimitExceededError } from "@mongodb-js/mcp-core";
5
+ import { sleep } from "./utils.js";
6
+ /**
7
+ * Session error codes as they were served to 2025-era clients on main; the
8
+ * shared constants were removed from mcp-core with the session surface, so
9
+ * the legacy path keeps its wire contract locally.
10
+ */
11
+ const JSON_RPC_ERROR_CODE_SESSION_ID_REQUIRED = -32001;
12
+ const JSON_RPC_ERROR_CODE_SESSION_ID_INVALID = -32002;
13
+ const JSON_RPC_ERROR_CODE_SESSION_NOT_FOUND = -32003;
14
+ const JSON_RPC_ERROR_CODE_INVALID_REQUEST = -32004;
15
+ const JSON_RPC_ERROR_CODE_DISALLOWED_EXTERNAL_SESSION = -32005;
16
+ const JSON_RPC_ERROR_CODE_SESSION_LIMIT_EXCEEDED = -32006;
17
+ /** Guards against oversized `mcp-session-id` headers (e.g. an abusive client). */
18
+ const MAX_SESSION_ID_LENGTH = 512;
19
+ /**
20
+ * Serves 2025-era (legacy) MCP requests over streamable HTTP **sessionfully**.
21
+ *
22
+ * The 2026-07-28 protocol is stateless and carries client identity per request
23
+ * in the `_meta` envelope. The 2025 protocol is not: it declares the client's
24
+ * negotiated capabilities (incl. `elicitation`) in the `initialize` handshake
25
+ * and delivers elicitation as real server→client requests. Both need a live,
26
+ * correlated transport, which is exactly what the SDK's legacy elicitation
27
+ * shim relies on. The SDK's `legacy: 'stateless'` mode cannot do that (each
28
+ * request gets a fresh instance: no capabilities, no return channel).
29
+ *
30
+ * This handler owns that sessionful lifecycle: an `initialize` POST creates a
31
+ * session (a connected transport/server pair), later requests and the SSE idle
32
+ * stream reuse the session's transport (the shim's return channel), and a
33
+ * DELETE closes it. Sessions are bounded by the injected `sessionStore`.
34
+ * Keeping it isolated from {@link MCPHttpServer} leaves the modern stateless
35
+ * path clean.
36
+ */
37
+ export class LegacyMcpHttpHandler {
38
+ constructor({ createServer, logger, http, sessionStore, externallyManagedSessions = false, }) {
39
+ /** Serializes concurrent initialization of the same session id (see {@link createSession}). */
40
+ this.pendingInitializations = new Map();
41
+ this.createServer = createServer;
42
+ this.logger = logger;
43
+ this.http = http;
44
+ this.sessions = sessionStore;
45
+ this.externallyManagedSessions = externallyManagedSessions;
46
+ }
47
+ /** Closes every live session (e.g. on server shutdown). */
48
+ async close() {
49
+ await this.sessions.closeAllSessions();
50
+ }
51
+ buildTransportContext(req) {
52
+ return {
53
+ headers: req.headers,
54
+ query: req.query,
55
+ // The explicit auth state of this request. Legacy serving does not
56
+ // normalize authInfo itself; hosts supply it via `req.auth` when
57
+ // using authenticated mode.
58
+ authInfo: req.auth
59
+ ? { mode: "authenticated", state: req.auth }
60
+ : { mode: "unauthenticated" },
61
+ protocol: "legacy",
62
+ };
63
+ }
64
+ /** Reports a session error the way main did: same codes, messages, and statuses. */
65
+ reportSessionError(res, errorCode) {
66
+ let message;
67
+ let statusCode = 400;
68
+ switch (errorCode) {
69
+ case JSON_RPC_ERROR_CODE_SESSION_ID_REQUIRED:
70
+ message = "session id is required";
71
+ break;
72
+ case JSON_RPC_ERROR_CODE_SESSION_ID_INVALID:
73
+ message = "session id is invalid";
74
+ break;
75
+ case JSON_RPC_ERROR_CODE_INVALID_REQUEST:
76
+ message = "invalid request";
77
+ break;
78
+ case JSON_RPC_ERROR_CODE_SESSION_NOT_FOUND:
79
+ message = "session not found";
80
+ statusCode = 404;
81
+ break;
82
+ case JSON_RPC_ERROR_CODE_DISALLOWED_EXTERNAL_SESSION:
83
+ message = "cannot provide sessionId when externally managed sessions are disabled";
84
+ break;
85
+ case JSON_RPC_ERROR_CODE_SESSION_LIMIT_EXCEEDED:
86
+ message = "server has reached the maximum number of concurrent sessions, try again later";
87
+ statusCode = 503;
88
+ break;
89
+ default:
90
+ message = "unknown error";
91
+ statusCode = 500;
92
+ }
93
+ res.status(statusCode).json({
94
+ jsonrpc: "2.0",
95
+ error: {
96
+ code: errorCode,
97
+ message,
98
+ },
99
+ });
100
+ }
101
+ /**
102
+ * Keeps the session's SSE idle stream alive through proxies and reaps dead
103
+ * clients: pings every 30s and closes the transport after 3 consecutive
104
+ * failures. Not started in JSON response mode, where connections are
105
+ * short-lived and pings aren't needed.
106
+ */
107
+ async startKeepAliveLoop({ transport, signal, }) {
108
+ if (this.http.responseType === "json") {
109
+ return;
110
+ }
111
+ let failedPings = 0;
112
+ while (!signal.aborted) {
113
+ try {
114
+ this.logger.debug({
115
+ id: LogId.streamableHttpTransportKeepAlive,
116
+ context: "streamableHttpTransport",
117
+ message: "Sending ping",
118
+ });
119
+ await transport.send({
120
+ jsonrpc: "2.0",
121
+ method: "ping",
122
+ });
123
+ failedPings = 0;
124
+ }
125
+ catch (err) {
126
+ try {
127
+ failedPings++;
128
+ this.logger.warning({
129
+ id: LogId.streamableHttpTransportKeepAliveFailure,
130
+ context: "streamableHttpTransport",
131
+ message: `Error sending ping (attempt #${failedPings}): ${err instanceof Error ? err.message : String(err)}`,
132
+ });
133
+ if (failedPings > 3) {
134
+ await transport.close();
135
+ return;
136
+ }
137
+ }
138
+ catch {
139
+ // Ignore the error of the transport close
140
+ }
141
+ }
142
+ await sleep(30000, { signal });
143
+ }
144
+ }
145
+ /**
146
+ * Creates a fresh session (or returns the existing / in-flight one for an
147
+ * externally managed id). Concurrent initializations of the same session id
148
+ * are serialized so they don't race on `addSession` (see
149
+ * {@link createSessionInstance}).
150
+ */
151
+ async createSession(req, providedSessionId, isImplicitInitialization = false) {
152
+ const sessionId = providedSessionId ?? getRandomUUID();
153
+ // An externally managed session may already exist in the store (e.g. a
154
+ // client re-sends `initialize` for the same id). Reuse it rather than
155
+ // creating a duplicate, which would throw "Session already exists".
156
+ const existingTransport = await this.sessions.getSession(sessionId, req.headers);
157
+ if (existingTransport) {
158
+ return existingTransport;
159
+ }
160
+ // Another request is already initializing this id; wait for it and reuse
161
+ // its transport instead of racing it.
162
+ const pendingInit = this.pendingInitializations.get(sessionId);
163
+ if (pendingInit) {
164
+ this.logger.debug({
165
+ id: LogId.streamableHttpTransportSessionNotFound,
166
+ context: "streamableHttpTransport",
167
+ message: `Session with ID ${sessionId} is already being initialized, waiting`,
168
+ attributes: { ...requestIdAttr(req.headers) },
169
+ });
170
+ try {
171
+ return await pendingInit;
172
+ }
173
+ catch {
174
+ // The initializer handles its own error; fall through to retry.
175
+ }
176
+ }
177
+ const initPromise = this.createSessionInstance(req, sessionId, isImplicitInitialization);
178
+ this.pendingInitializations.set(sessionId, initPromise);
179
+ try {
180
+ return await initPromise;
181
+ }
182
+ finally {
183
+ this.pendingInitializations.delete(sessionId);
184
+ }
185
+ }
186
+ /** Builds the server/transport pair and registers it in the session store. */
187
+ async createSessionInstance(req, sessionId, isImplicitInitialization) {
188
+ const transport = new NodeStreamableHTTPServerTransport({
189
+ sessionIdGenerator: () => sessionId,
190
+ enableJsonResponse: this.http.responseType === "json",
191
+ });
192
+ // HACK: When we're implicitly re-initializing an externally managed
193
+ // session, mark the transport as already initialized so the SDK does
194
+ // not reject follow-up requests with "transport not initialized".
195
+ // Guard the internal field: if the SDK's transport shape changes, the
196
+ // session degrades gracefully rather than throwing before the request
197
+ // can be answered.
198
+ if (isImplicitInitialization) {
199
+ const internalTransport = transport._webStandardTransport;
200
+ if (internalTransport) {
201
+ internalTransport._initialized = true;
202
+ internalTransport.sessionId = sessionId;
203
+ }
204
+ }
205
+ const server = (await this.createServer(this.buildTransportContext(req)));
206
+ // Admit (check cap, evict LRU idle victim). A rejection admits nothing.
207
+ try {
208
+ await this.sessions.addSession({ sessionId, transport, logger: this.logger, headers: req.headers });
209
+ }
210
+ catch (error) {
211
+ // Dispose the un-connected server we built for a rejected session.
212
+ await server.close().catch(() => undefined);
213
+ throw error;
214
+ }
215
+ const keepAliveController = new AbortController();
216
+ void this.startKeepAliveLoop({ transport, signal: keepAliveController.signal });
217
+ // When the transport closes (client disconnect or store-initiated
218
+ // eviction/timeout), tear down the server and remove the session.
219
+ transport.onclose = () => {
220
+ keepAliveController.abort();
221
+ void server
222
+ .close()
223
+ .catch((error) => {
224
+ this.logger.error({
225
+ id: LogId.streamableHttpTransportCloseFailure,
226
+ context: "streamableHttpTransport",
227
+ message: `Error closing legacy session ${sessionId}: ${error instanceof Error ? error.message : String(error)}`,
228
+ });
229
+ })
230
+ .finally(() => {
231
+ // The store removes the session before it closes the
232
+ // transport, so on store-initiated closes (eviction, idle
233
+ // timeout) the session is already gone — nothing to do.
234
+ if (!this.sessions.hasSession(sessionId)) {
235
+ return;
236
+ }
237
+ this.sessions.closeSession({ sessionId, reason: "transport_closed" }).catch((error) => {
238
+ this.logger.error({
239
+ id: LogId.streamableHttpTransportCloseFailure,
240
+ context: "streamableHttpTransport",
241
+ message: `Error removing legacy session ${sessionId}: ${error instanceof Error ? error.message : String(error)}`,
242
+ });
243
+ });
244
+ });
245
+ };
246
+ try {
247
+ await server.connect(transport);
248
+ }
249
+ catch (error) {
250
+ // Don't leave a never-connected session registered; closing the
251
+ // transport triggers the onclose teardown above.
252
+ await this.sessions.closeSession({ sessionId, reason: "unknown" }).catch(() => undefined);
253
+ await server.close().catch(() => undefined);
254
+ throw error;
255
+ }
256
+ if (isImplicitInitialization) {
257
+ await this.restoreNegotiatedClientState(server, sessionId, req.headers);
258
+ }
259
+ else {
260
+ this.captureNegotiatedClientStateOnInitialize(server, sessionId, req.headers);
261
+ }
262
+ return transport;
263
+ }
264
+ /**
265
+ * Serves a 2025-era request. POSTs that carry no session begin a new session
266
+ * (the `initialize` handshake); POSTs/GETs/DELETEs that carry a session id
267
+ * are routed to the stored transport, preserving the client's negotiated
268
+ * state and providing the SSE return channel for server→client requests.
269
+ */
270
+ async handle(req, res) {
271
+ const sessionId = req.headers["mcp-session-id"];
272
+ if (req.method === "GET") {
273
+ // The SSE idle stream only exists in SSE response mode.
274
+ if (this.http.responseType === "json") {
275
+ res.status(405).set("Allow", ["POST", "DELETE"]).send("Method Not Allowed");
276
+ return;
277
+ }
278
+ await this.handleSessionRequest(req, res, sessionId);
279
+ return;
280
+ }
281
+ if (req.method === "DELETE") {
282
+ await this.handleSessionRequest(req, res, sessionId);
283
+ return;
284
+ }
285
+ // POST /mcp.
286
+ if (sessionId && typeof sessionId !== "string") {
287
+ this.reportSessionError(res, JSON_RPC_ERROR_CODE_SESSION_ID_INVALID);
288
+ return;
289
+ }
290
+ if (typeof sessionId === "string" && sessionId.length > MAX_SESSION_ID_LENGTH) {
291
+ this.logger.debug({
292
+ id: LogId.streamableHttpTransportSessionNotFound,
293
+ context: "streamableHttpTransport",
294
+ message: `Session ID exceeds maximum length ${MAX_SESSION_ID_LENGTH}`,
295
+ attributes: { ...requestIdAttr(req.headers) },
296
+ });
297
+ this.reportSessionError(res, JSON_RPC_ERROR_CODE_SESSION_ID_INVALID);
298
+ return;
299
+ }
300
+ if (isInitializeRequest(req.body)) {
301
+ const resolvedSessionId = typeof sessionId === "string" && this.externallyManagedSessions ? sessionId : undefined;
302
+ if (sessionId && !resolvedSessionId) {
303
+ // A client-supplied session id is only honored when externally
304
+ // managed sessions are enabled; otherwise it is rejected.
305
+ this.logger.debug({
306
+ id: LogId.streamableHttpTransportDisallowedExternalSessionError,
307
+ context: "streamableHttpTransport",
308
+ message: `Client provided session ID ${sessionId}, but externallyManagedSessions is disabled`,
309
+ attributes: { ...requestIdAttr(req.headers) },
310
+ });
311
+ this.reportSessionError(res, JSON_RPC_ERROR_CODE_DISALLOWED_EXTERNAL_SESSION);
312
+ return;
313
+ }
314
+ let transport;
315
+ try {
316
+ transport = await this.createSession(req, resolvedSessionId);
317
+ }
318
+ catch (error) {
319
+ if (error instanceof SessionLimitExceededError) {
320
+ this.logger.warning({
321
+ id: LogId.streamableHttpTransportRequestFailure,
322
+ context: "streamableHttpTransport",
323
+ message: `Rejecting legacy session startup: ${error.message}`,
324
+ attributes: { ...requestIdAttr(req.headers) },
325
+ });
326
+ this.reportSessionError(res, JSON_RPC_ERROR_CODE_SESSION_LIMIT_EXCEEDED);
327
+ return;
328
+ }
329
+ throw error;
330
+ }
331
+ await transport.handleRequest(req, res, req.body);
332
+ return;
333
+ }
334
+ if (!sessionId) {
335
+ this.reportSessionError(res, JSON_RPC_ERROR_CODE_INVALID_REQUEST);
336
+ return;
337
+ }
338
+ await this.handleSessionRequest(req, res, sessionId);
339
+ }
340
+ /** Routes a session-carrying request to its live transport; reports a session error when missing. */
341
+ async handleSessionRequest(req, res, sessionId) {
342
+ if (!sessionId) {
343
+ this.reportSessionError(res, JSON_RPC_ERROR_CODE_SESSION_ID_REQUIRED);
344
+ return;
345
+ }
346
+ if (typeof sessionId !== "string") {
347
+ this.reportSessionError(res, JSON_RPC_ERROR_CODE_SESSION_ID_INVALID);
348
+ return;
349
+ }
350
+ let transport = await this.sessions.getSession(sessionId, req.headers);
351
+ if (!transport) {
352
+ if (!this.externallyManagedSessions) {
353
+ this.logger.debug({
354
+ id: LogId.streamableHttpTransportSessionNotFound,
355
+ context: "streamableHttpTransport",
356
+ message: `Session with ID ${sessionId} not found`,
357
+ attributes: { ...requestIdAttr(req.headers) },
358
+ });
359
+ this.reportSessionError(res, JSON_RPC_ERROR_CODE_SESSION_NOT_FOUND);
360
+ return;
361
+ }
362
+ // Externally managed sessions may be implicitly re-initialized: a
363
+ // session held externally can arrive at this pod without being in
364
+ // the store. Build a fresh session under the supplied id and
365
+ // restore the client's previously negotiated capabilities (if the
366
+ // store persists them) instead of rejecting the request.
367
+ this.logger.debug({
368
+ id: LogId.streamableHttpTransportSessionNotFound,
369
+ context: "streamableHttpTransport",
370
+ message: `Session with ID ${sessionId} not found, implicitly re-initializing`,
371
+ attributes: { ...requestIdAttr(req.headers) },
372
+ });
373
+ transport = await this.createSession(req, sessionId, true);
374
+ }
375
+ await transport.handleRequest(req, res, req.body);
376
+ }
377
+ /**
378
+ * Restores the client state negotiated during the session's original
379
+ * `initialize` onto an implicitly re-initialized server. The session's
380
+ * server on this pod never saw the client's `initialize` request, so
381
+ * without this it would treat the client as capability-less — e.g.
382
+ * skipping confirmation elicitation for destructive tools. No-op when the
383
+ * session store does not persist negotiated client state.
384
+ */
385
+ async restoreNegotiatedClientState(server, sessionId, headers) {
386
+ let state;
387
+ try {
388
+ state = await this.sessions.loadNegotiatedClientState(sessionId, headers);
389
+ }
390
+ catch (error) {
391
+ // The restored session stays usable; it just behaves as if the
392
+ // client had no optional capabilities.
393
+ this.logger.warning({
394
+ id: LogId.streamableHttpTransportClientStateRestoreFailure,
395
+ context: "streamableHttpTransport",
396
+ message: `Failed to restore negotiated client state for session ${sessionId}: ${error instanceof Error ? error.message : String(error)}`,
397
+ attributes: { ...requestIdAttr(headers) },
398
+ });
399
+ return;
400
+ }
401
+ if (!state) {
402
+ return;
403
+ }
404
+ // HACK: the SDK offers no supported way to seed a Server with a
405
+ // previously negotiated initialization, so we write the private fields
406
+ // the initialize handler would have populated.
407
+ const protocolServer = server.mcpServer.server;
408
+ protocolServer._clientCapabilities = state.clientCapabilities;
409
+ protocolServer._clientVersion = state.clientInfo;
410
+ }
411
+ /**
412
+ * Arranges for the client state negotiated by a real `initialize` exchange
413
+ * to be persisted through the session store, so implicit re-initializations
414
+ * of this session can restore it. Wraps the `oninitialized` callback
415
+ * installed by `server.connect`, hence must run after it.
416
+ */
417
+ captureNegotiatedClientStateOnInitialize(server, sessionId, headers) {
418
+ const protocolServer = server.mcpServer.server;
419
+ const originalOnInitialized = protocolServer.oninitialized;
420
+ protocolServer.oninitialized = () => {
421
+ originalOnInitialized?.();
422
+ const state = {
423
+ clientCapabilities: protocolServer.getClientCapabilities(),
424
+ clientInfo: protocolServer.getClientVersion(),
425
+ };
426
+ this.sessions.saveNegotiatedClientState(sessionId, state, headers).catch((error) => {
427
+ this.logger.warning({
428
+ id: LogId.streamableHttpTransportClientStateSaveFailure,
429
+ context: "streamableHttpTransport",
430
+ message: `Failed to save negotiated client state for session ${sessionId}: ${error instanceof Error ? error.message : String(error)}`,
431
+ attributes: { ...requestIdAttr(headers) },
432
+ });
433
+ });
434
+ };
435
+ }
436
+ }
437
+ //# sourceMappingURL=legacyMcpHttpHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacyMcpHttpHandler.js","sourceRoot":"","sources":["../src/legacyMcpHttpHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAgD,MAAM,8BAA8B,CAAC;AACjH,OAAO,EAAE,iCAAiC,EAAE,MAAM,4BAA4B,CAAC;AAU/E,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAE,yBAAyB,EAAsB,MAAM,sBAAsB,CAAC;AACrF,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC;;;;GAIG;AACH,MAAM,uCAAuC,GAAG,CAAC,KAAK,CAAC;AACvD,MAAM,sCAAsC,GAAG,CAAC,KAAK,CAAC;AACtD,MAAM,qCAAqC,GAAG,CAAC,KAAK,CAAC;AACrD,MAAM,mCAAmC,GAAG,CAAC,KAAK,CAAC;AACnD,MAAM,+CAA+C,GAAG,CAAC,KAAK,CAAC;AAC/D,MAAM,0CAA0C,GAAG,CAAC,KAAK,CAAC;AAE1D,kFAAkF;AAClF,MAAM,qBAAqB,GAAG,GAAG,CAAC;AA4DlC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,oBAAoB;IAS7B,YAAY,EACR,YAAY,EACZ,MAAM,EACN,IAAI,EACJ,YAAY,EACZ,yBAAyB,GAAG,KAAK,GACP;QAT9B,+FAA+F;QAC9E,2BAAsB,GAAG,IAAI,GAAG,EAAsD,CAAC;QASpG,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,YAAY,CAAC;QAC7B,IAAI,CAAC,yBAAyB,GAAG,yBAAyB,CAAC;IAC/D,CAAC;IAED,2DAA2D;IACpD,KAAK,CAAC,KAAK;QACd,MAAM,IAAI,CAAC,QAAQ,CAAC,gBAAgB,EAAE,CAAC;IAC3C,CAAC;IAEO,qBAAqB,CAAC,GAAoB;QAC9C,OAAO;YACH,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,KAAK,EAAE,GAAG,CAAC,KAAsD;YACjE,mEAAmE;YACnE,iEAAiE;YACjE,4BAA4B;YAC5B,QAAQ,EAAG,GAAoD,CAAC,IAAI;gBAChE,CAAC,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAG,GAAmD,CAAC,IAAI,EAAE;gBAC7F,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE;YACjC,QAAQ,EAAE,QAAQ;SACrB,CAAC;IACN,CAAC;IAED,oFAAoF;IAC5E,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;IAED;;;;;OAKG;IACK,KAAK,CAAC,kBAAkB,CAAC,EAC7B,SAAS,EACT,MAAM,GAIT;QACG,IAAI,IAAI,CAAC,IAAI,CAAC,YAAY,KAAK,MAAM,EAAE,CAAC;YACpC,OAAO;QACX,CAAC;QAED,IAAI,WAAW,GAAG,CAAC,CAAC;QAEpB,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACrB,IAAI,CAAC;gBACD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;oBACd,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,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;wBAChB,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;;;;;OAKG;IACK,KAAK,CAAC,aAAa,CACvB,GAAoB,EACpB,iBAA0B,EAC1B,wBAAwB,GAAG,KAAK;QAEhC,MAAM,SAAS,GAAG,iBAAiB,IAAI,aAAa,EAAE,CAAC;QAEvD,uEAAuE;QACvE,sEAAsE;QACtE,oEAAoE;QACpE,MAAM,iBAAiB,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QACjF,IAAI,iBAAiB,EAAE,CAAC;YACpB,OAAO,iBAAiB,CAAC;QAC7B,CAAC;QAED,yEAAyE;QACzE,sCAAsC;QACtC,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,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE;aAChD,CAAC,CAAC;YACH,IAAI,CAAC;gBACD,OAAO,MAAM,WAAW,CAAC;YAC7B,CAAC;YAAC,MAAM,CAAC;gBACL,gEAAgE;YACpE,CAAC;QACL,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,CAAC,qBAAqB,CAAC,GAAG,EAAE,SAAS,EAAE,wBAAwB,CAAC,CAAC;QACzF,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QACxD,IAAI,CAAC;YACD,OAAO,MAAM,WAAW,CAAC;QAC7B,CAAC;gBAAS,CAAC;YACP,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAClD,CAAC;IACL,CAAC;IAED,8EAA8E;IACtE,KAAK,CAAC,qBAAqB,CAC/B,GAAoB,EACpB,SAAiB,EACjB,wBAAiC;QAEjC,MAAM,SAAS,GAAG,IAAI,iCAAiC,CAAC;YACpD,kBAAkB,EAAE,GAAW,EAAE,CAAC,SAAS;YAC3C,kBAAkB,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,KAAK,MAAM;SACxD,CAAC,CAAC;QAEH,oEAAoE;QACpE,qEAAqE;QACrE,kEAAkE;QAClE,sEAAsE;QACtE,sEAAsE;QACtE,mBAAmB;QACnB,IAAI,wBAAwB,EAAE,CAAC;YAC3B,MAAM,iBAAiB,GACnB,SAGH,CAAC,qBAAqB,CAAC;YACxB,IAAI,iBAAiB,EAAE,CAAC;gBACpB,iBAAiB,CAAC,YAAY,GAAG,IAAI,CAAC;gBACtC,iBAAiB,CAAC,SAAS,GAAG,SAAS,CAAC;YAC5C,CAAC;QACL,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC,CAAgC,CAAC;QAEzG,wEAAwE;QACxE,IAAI,CAAC;YACD,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACxG,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,mEAAmE;YACnE,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;YAC5C,MAAM,KAAK,CAAC;QAChB,CAAC;QAED,MAAM,mBAAmB,GAAG,IAAI,eAAe,EAAE,CAAC;QAClD,KAAK,IAAI,CAAC,kBAAkB,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,mBAAmB,CAAC,MAAM,EAAE,CAAC,CAAC;QAChF,kEAAkE;QAClE,kEAAkE;QAClE,SAAS,CAAC,OAAO,GAAG,GAAS,EAAE;YAC3B,mBAAmB,CAAC,KAAK,EAAE,CAAC;YAC5B,KAAK,MAAM;iBACN,KAAK,EAAE;iBACP,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;gBACtB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;oBACd,EAAE,EAAE,KAAK,CAAC,mCAAmC;oBAC7C,OAAO,EAAE,yBAAyB;oBAClC,OAAO,EAAE,gCAAgC,SAAS,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;iBAClH,CAAC,CAAC;YACP,CAAC,CAAC;iBACD,OAAO,CAAC,GAAG,EAAE;gBACV,qDAAqD;gBACrD,0DAA0D;gBAC1D,wDAAwD;gBACxD,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;oBACvC,OAAO;gBACX,CAAC;gBACD,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;oBAC3F,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;wBACd,EAAE,EAAE,KAAK,CAAC,mCAAmC;wBAC7C,OAAO,EAAE,yBAAyB;wBAClC,OAAO,EAAE,iCAAiC,SAAS,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;qBACnH,CAAC,CAAC;gBACP,CAAC,CAAC,CAAC;YACP,CAAC,CAAC,CAAC;QACX,CAAC,CAAC;QAEF,IAAI,CAAC;YACD,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,gEAAgE;YAChE,iDAAiD;YACjD,MAAM,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;YAC1F,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;YAC5C,MAAM,KAAK,CAAC;QAChB,CAAC;QAED,IAAI,wBAAwB,EAAE,CAAC;YAC3B,MAAM,IAAI,CAAC,4BAA4B,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QAC5E,CAAC;aAAM,CAAC;YACJ,IAAI,CAAC,wCAAwC,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QAClF,CAAC;QAED,OAAO,SAAS,CAAC;IACrB,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,MAAM,CAAC,GAAoB,EAAE,GAAqB;QAC3D,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAEhD,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;YACvB,wDAAwD;YACxD,IAAI,IAAI,CAAC,IAAI,CAAC,YAAY,KAAK,MAAM,EAAE,CAAC;gBACpC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;gBAC5E,OAAO;YACX,CAAC;YACD,MAAM,IAAI,CAAC,oBAAoB,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC;YACrD,OAAO;QACX,CAAC;QAED,IAAI,GAAG,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC1B,MAAM,IAAI,CAAC,oBAAoB,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC;YACrD,OAAO;QACX,CAAC;QAED,aAAa;QACb,IAAI,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;YAC7C,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,sCAAsC,CAAC,CAAC;YACrE,OAAO;QACX,CAAC;QACD,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,GAAG,qBAAqB,EAAE,CAAC;YAC5E,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;gBACd,EAAE,EAAE,KAAK,CAAC,sCAAsC;gBAChD,OAAO,EAAE,yBAAyB;gBAClC,OAAO,EAAE,qCAAqC,qBAAqB,EAAE;gBACrE,UAAU,EAAE,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE;aAChD,CAAC,CAAC;YACH,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,sCAAsC,CAAC,CAAC;YACrE,OAAO;QACX,CAAC;QAED,IAAI,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAChC,MAAM,iBAAiB,GACnB,OAAO,SAAS,KAAK,QAAQ,IAAI,IAAI,CAAC,yBAAyB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;YAC5F,IAAI,SAAS,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBAClC,+DAA+D;gBAC/D,0DAA0D;gBAC1D,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;oBACd,EAAE,EAAE,KAAK,CAAC,qDAAqD;oBAC/D,OAAO,EAAE,yBAAyB;oBAClC,OAAO,EAAE,8BAA8B,SAAS,6CAA6C;oBAC7F,UAAU,EAAE,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE;iBAChD,CAAC,CAAC;gBACH,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,+CAA+C,CAAC,CAAC;gBAC9E,OAAO;YACX,CAAC;YAED,IAAI,SAA4C,CAAC;YACjD,IAAI,CAAC;gBACD,SAAS,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;YACjE,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,IAAI,KAAK,YAAY,yBAAyB,EAAE,CAAC;oBAC7C,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;wBAChB,EAAE,EAAE,KAAK,CAAC,qCAAqC;wBAC/C,OAAO,EAAE,yBAAyB;wBAClC,OAAO,EAAE,qCAAqC,KAAK,CAAC,OAAO,EAAE;wBAC7D,UAAU,EAAE,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE;qBAChD,CAAC,CAAC;oBACH,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,0CAA0C,CAAC,CAAC;oBACzE,OAAO;gBACX,CAAC;gBACD,MAAM,KAAK,CAAC;YAChB,CAAC;YACD,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;YAClD,OAAO;QACX,CAAC;QAED,IAAI,CAAC,SAAS,EAAE,CAAC;YACb,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,mCAAmC,CAAC,CAAC;YAClE,OAAO;QACX,CAAC;QAED,MAAM,IAAI,CAAC,oBAAoB,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC;IACzD,CAAC;IAED,qGAAqG;IAC7F,KAAK,CAAC,oBAAoB,CAC9B,GAAoB,EACpB,GAAqB,EACrB,SAAwC;QAExC,IAAI,CAAC,SAAS,EAAE,CAAC;YACb,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,uCAAuC,CAAC,CAAC;YACtE,OAAO;QACX,CAAC;QACD,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;YAChC,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,sCAAsC,CAAC,CAAC;YACrE,OAAO;QACX,CAAC;QACD,IAAI,SAAS,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QACvE,IAAI,CAAC,SAAS,EAAE,CAAC;YACb,IAAI,CAAC,IAAI,CAAC,yBAAyB,EAAE,CAAC;gBAClC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;oBACd,EAAE,EAAE,KAAK,CAAC,sCAAsC;oBAChD,OAAO,EAAE,yBAAyB;oBAClC,OAAO,EAAE,mBAAmB,SAAS,YAAY;oBACjD,UAAU,EAAE,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE;iBAChD,CAAC,CAAC;gBACH,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,qCAAqC,CAAC,CAAC;gBACpE,OAAO;YACX,CAAC;YAED,kEAAkE;YAClE,kEAAkE;YAClE,6DAA6D;YAC7D,kEAAkE;YAClE,yDAAyD;YACzD,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,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE;aAChD,CAAC,CAAC;YACH,SAAS,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;QAC/D,CAAC;QACD,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IACtD,CAAC;IAED;;;;;;;OAOG;IACK,KAAK,CAAC,4BAA4B,CACtC,MAAwB,EACxB,SAAiB,EACjB,OAAgC;QAEhC,IAAI,KAAwC,CAAC;QAC7C,IAAI,CAAC;YACD,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,yBAAyB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAC9E,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,gEAAgE;QAChE,uEAAuE;QACvE,+CAA+C;QAC/C,MAAM,cAAc,GAAG,MAAM,CAAC,SAAS,CAAC,MAGvC,CAAC;QACF,cAAc,CAAC,mBAAmB,GAAG,KAAK,CAAC,kBAAkB,CAAC;QAC9D,cAAc,CAAC,cAAc,GAAG,KAAK,CAAC,UAAU,CAAC;IACrD,CAAC;IAED;;;;;OAKG;IACK,wCAAwC,CAC5C,MAAwB,EACxB,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,QAAQ,CAAC,yBAAyB,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;gBACxF,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;CACJ"}