mcp-proxy 6.5.4 → 6.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -2
- package/dist/bin/mcp-proxy.mjs +17 -2
- package/dist/bin/mcp-proxy.mjs.map +1 -1
- package/dist/index.d.mts +41 -2
- package/dist/index.mjs +1 -1
- package/dist/{stdio-BQunCFor.mjs → stdio-Be0Fx2iV.mjs} +28 -5
- package/dist/stdio-Be0Fx2iV.mjs.map +1 -0
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/InMemoryEventStore.test.ts +96 -0
- package/src/InMemoryEventStore.ts +51 -0
- package/src/bin/mcp-proxy.ts +21 -2
- package/src/index.ts +2 -1
- package/src/startHTTPServer.test.ts +75 -0
- package/src/startHTTPServer.ts +42 -4
- package/dist/stdio-BQunCFor.mjs.map +0 -1
package/jsr.json
CHANGED
package/package.json
CHANGED
|
@@ -237,4 +237,100 @@ describe("InMemoryEventStore", () => {
|
|
|
237
237
|
secondSpy.mockRestore();
|
|
238
238
|
}
|
|
239
239
|
});
|
|
240
|
+
|
|
241
|
+
it("evicts the oldest events once maxEvents is exceeded", async () => {
|
|
242
|
+
const store = new InMemoryEventStore({ maxEvents: 3 });
|
|
243
|
+
const streamId = "bounded-stream";
|
|
244
|
+
|
|
245
|
+
const eventIds: string[] = [];
|
|
246
|
+
for (let i = 0; i < 5; i++) {
|
|
247
|
+
eventIds.push(
|
|
248
|
+
await store.storeEvent(streamId, {
|
|
249
|
+
id: i,
|
|
250
|
+
jsonrpc: "2.0",
|
|
251
|
+
method: `step/${i}`,
|
|
252
|
+
}),
|
|
253
|
+
);
|
|
254
|
+
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Only the 3 most recently stored events survive.
|
|
258
|
+
expect(store.size).toBe(3);
|
|
259
|
+
|
|
260
|
+
// The two oldest events were evicted, so resuming from them is no
|
|
261
|
+
// longer possible - the caller falls back to a fresh stream.
|
|
262
|
+
for (const evictedId of eventIds.slice(0, 2)) {
|
|
263
|
+
const result = await store.replayEventsAfter(evictedId, {
|
|
264
|
+
send: vi.fn(),
|
|
265
|
+
});
|
|
266
|
+
expect(result).toBe("");
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Resuming from a surviving event still replays what came after it.
|
|
270
|
+
const replayed: JSONRPCMessage[] = [];
|
|
271
|
+
const returnedStreamId = await store.replayEventsAfter(eventIds[2], {
|
|
272
|
+
send: async (_eventId, message) => {
|
|
273
|
+
replayed.push(message);
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
expect(returnedStreamId).toBe(streamId);
|
|
277
|
+
expect(replayed).toHaveLength(2);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it("caps total retained events across all streams, not per stream", async () => {
|
|
281
|
+
const store = new InMemoryEventStore({ maxEvents: 4 });
|
|
282
|
+
|
|
283
|
+
for (let i = 0; i < 3; i++) {
|
|
284
|
+
await store.storeEvent("stream-a", {
|
|
285
|
+
id: i,
|
|
286
|
+
jsonrpc: "2.0",
|
|
287
|
+
method: `a/${i}`,
|
|
288
|
+
});
|
|
289
|
+
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
290
|
+
}
|
|
291
|
+
for (let i = 0; i < 3; i++) {
|
|
292
|
+
await store.storeEvent("stream-b", {
|
|
293
|
+
id: i,
|
|
294
|
+
jsonrpc: "2.0",
|
|
295
|
+
method: `b/${i}`,
|
|
296
|
+
});
|
|
297
|
+
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// 6 events were stored across two streams; the store-wide cap still holds.
|
|
301
|
+
expect(store.size).toBe(4);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it("defaults to a bounded size when maxEvents is not configured", async () => {
|
|
305
|
+
const store = new InMemoryEventStore();
|
|
306
|
+
const streamId = "default-cap-stream";
|
|
307
|
+
|
|
308
|
+
for (let i = 0; i < 1010; i++) {
|
|
309
|
+
await store.storeEvent(streamId, {
|
|
310
|
+
id: i,
|
|
311
|
+
jsonrpc: "2.0",
|
|
312
|
+
method: "tools/list",
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
expect(store.size).toBe(1000);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it("rejects a non-positive maxEvents", () => {
|
|
320
|
+
expect(() => new InMemoryEventStore({ maxEvents: 0 })).toThrow(
|
|
321
|
+
/maxEvents must be at least 1/,
|
|
322
|
+
);
|
|
323
|
+
expect(() => new InMemoryEventStore({ maxEvents: -1 })).toThrow(
|
|
324
|
+
/maxEvents must be at least 1/,
|
|
325
|
+
);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it("rejects a NaN maxEvents instead of silently disabling eviction", () => {
|
|
329
|
+
// A naive `maxEvents < 1` check lets NaN through (every comparison with
|
|
330
|
+
// NaN is false), which would make `size > maxEvents` never trigger and
|
|
331
|
+
// silently reintroduce unbounded growth.
|
|
332
|
+
expect(() => new InMemoryEventStore({ maxEvents: NaN })).toThrow(
|
|
333
|
+
/maxEvents must be at least 1/,
|
|
334
|
+
);
|
|
335
|
+
});
|
|
240
336
|
});
|
|
@@ -6,17 +6,56 @@
|
|
|
6
6
|
import type { EventStore } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
7
7
|
import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
|
|
8
8
|
|
|
9
|
+
// A stream accumulates one event per server->client message (e.g. every
|
|
10
|
+
// tool call on a stateful session opens its own stream). With no cap, a
|
|
11
|
+
// single long-lived session climbs without bound - see
|
|
12
|
+
// https://github.com/punkpeye/mcp-proxy/issues/72.
|
|
13
|
+
const DEFAULT_MAX_EVENTS = 1000;
|
|
14
|
+
|
|
15
|
+
export interface InMemoryEventStoreOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Maximum number of events retained across all streams before the oldest
|
|
18
|
+
* are evicted (FIFO). Bounds memory use; older reconnect attempts beyond
|
|
19
|
+
* this window fall back to a fresh (non-resumed) stream instead of a replay.
|
|
20
|
+
*
|
|
21
|
+
* @default 1000
|
|
22
|
+
*/
|
|
23
|
+
maxEvents?: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
9
26
|
/**
|
|
10
27
|
* Simple in-memory implementation of the EventStore interface for resumability
|
|
11
28
|
* This is primarily intended for examples and testing, not for production use
|
|
12
29
|
* where a persistent storage solution would be more appropriate.
|
|
13
30
|
*/
|
|
14
31
|
export class InMemoryEventStore implements EventStore {
|
|
32
|
+
/**
|
|
33
|
+
* The number of events currently retained (for observability/testing).
|
|
34
|
+
*/
|
|
35
|
+
get size(): number {
|
|
36
|
+
return this.events.size;
|
|
37
|
+
}
|
|
15
38
|
private events: Map<string, { message: JSONRPCMessage; streamId: string }> =
|
|
16
39
|
new Map();
|
|
17
40
|
private lastTimestamp = 0;
|
|
18
41
|
private lastTimestampCounter = 0;
|
|
19
42
|
|
|
43
|
+
private readonly maxEvents: number;
|
|
44
|
+
|
|
45
|
+
constructor(options: InMemoryEventStoreOptions = {}) {
|
|
46
|
+
const maxEvents = options.maxEvents ?? DEFAULT_MAX_EVENTS;
|
|
47
|
+
|
|
48
|
+
// Written as `!(maxEvents >= 1)` rather than `maxEvents < 1` so NaN
|
|
49
|
+
// (every comparison with NaN is false) is rejected too - otherwise
|
|
50
|
+
// `size > maxEvents` would never trigger eviction and silently
|
|
51
|
+
// reintroduce unbounded growth.
|
|
52
|
+
if (!(maxEvents >= 1)) {
|
|
53
|
+
throw new Error("maxEvents must be at least 1");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this.maxEvents = maxEvents;
|
|
57
|
+
}
|
|
58
|
+
|
|
20
59
|
/**
|
|
21
60
|
* Replays events that occurred after a specific event ID
|
|
22
61
|
* Implements EventStore.replayEventsAfter
|
|
@@ -77,6 +116,18 @@ export class InMemoryEventStore implements EventStore {
|
|
|
77
116
|
|
|
78
117
|
this.events.set(eventId, { message, streamId });
|
|
79
118
|
|
|
119
|
+
// Map iterates in insertion order, so the first key is always the
|
|
120
|
+
// oldest surviving event - evicting it is a plain FIFO/ring buffer.
|
|
121
|
+
while (this.events.size > this.maxEvents) {
|
|
122
|
+
const oldestEventId = this.events.keys().next().value;
|
|
123
|
+
|
|
124
|
+
if (oldestEventId === undefined) {
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
this.events.delete(oldestEventId);
|
|
129
|
+
}
|
|
130
|
+
|
|
80
131
|
return eventId;
|
|
81
132
|
}
|
|
82
133
|
|
package/src/bin/mcp-proxy.ts
CHANGED
|
@@ -14,7 +14,6 @@ import { hideBin } from "yargs/helpers";
|
|
|
14
14
|
const require = createRequire(import.meta.url);
|
|
15
15
|
const packageJson = require("../../package.json") as { version: string };
|
|
16
16
|
|
|
17
|
-
import { InMemoryEventStore } from "../InMemoryEventStore.js";
|
|
18
17
|
import { proxyServer } from "../proxyServer.js";
|
|
19
18
|
import { SSEServer, startHTTPServer } from "../startHTTPServer.js";
|
|
20
19
|
import { StdioClientTransport } from "../StdioClientTransport.js";
|
|
@@ -70,6 +69,18 @@ const argv = await yargs(hideBin(process.argv))
|
|
|
70
69
|
describe: "The endpoint to listen on",
|
|
71
70
|
type: "string",
|
|
72
71
|
},
|
|
72
|
+
eventStore: {
|
|
73
|
+
default: true,
|
|
74
|
+
describe:
|
|
75
|
+
"Enable the streamable HTTP transport's resumability event store, which lets clients replay missed messages after a reconnect. Use --no-eventStore to disable it entirely for request/response-only deployments that don't need this and would rather avoid the memory overhead",
|
|
76
|
+
type: "boolean",
|
|
77
|
+
},
|
|
78
|
+
eventStoreMaxEvents: {
|
|
79
|
+
default: 1000,
|
|
80
|
+
describe:
|
|
81
|
+
"Maximum number of buffered events the resumability event store retains (per session) before it evicts the oldest; bounds memory use. Ignored when --no-eventStore is set",
|
|
82
|
+
type: "number",
|
|
83
|
+
},
|
|
73
84
|
gracefulShutdownTimeout: {
|
|
74
85
|
default: 5000,
|
|
75
86
|
describe: "The timeout (in milliseconds) for graceful shutdown",
|
|
@@ -149,6 +160,13 @@ const argv = await yargs(hideBin(process.argv))
|
|
|
149
160
|
.help()
|
|
150
161
|
.parseAsync();
|
|
151
162
|
|
|
163
|
+
if (!(argv.eventStoreMaxEvents >= 1)) {
|
|
164
|
+
console.error(
|
|
165
|
+
`Error: --eventStoreMaxEvents must be a number >= 1 (got ${String(argv.eventStoreMaxEvents)}). Use --no-eventStore to disable the event store instead.`,
|
|
166
|
+
);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
|
|
152
170
|
// Default Access-Control-Allow-Headers list — must stay in sync with
|
|
153
171
|
// `defaultCorsOptions.allowedHeaders` in src/startHTTPServer.ts.
|
|
154
172
|
const DEFAULT_ALLOWED_HEADERS = [
|
|
@@ -248,7 +266,8 @@ const proxy = async () => {
|
|
|
248
266
|
apiKey: argv.apiKey,
|
|
249
267
|
cors: corsOption,
|
|
250
268
|
createServer,
|
|
251
|
-
eventStore:
|
|
269
|
+
eventStore: argv.eventStore ? undefined : false,
|
|
270
|
+
eventStoreMaxEvents: argv.eventStoreMaxEvents,
|
|
252
271
|
host: argv.host,
|
|
253
272
|
keepAliveTimeout: argv.keepAliveTimeout,
|
|
254
273
|
port: argv.port,
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export type { AuthConfig } from "./authentication.js";
|
|
2
2
|
export { AuthenticationMiddleware } from "./authentication.js";
|
|
3
|
+
export type { InMemoryEventStoreOptions } from "./InMemoryEventStore.js";
|
|
3
4
|
export { InMemoryEventStore } from "./InMemoryEventStore.js";
|
|
4
5
|
export { proxyServer } from "./proxyServer.js";
|
|
5
|
-
export type { CorsOptions } from "./startHTTPServer.js";
|
|
6
|
+
export type { CorsOptions, EventStoreOption } from "./startHTTPServer.js";
|
|
6
7
|
export { startHTTPServer } from "./startHTTPServer.js";
|
|
7
8
|
export { ServerType, startStdioServer } from "./startStdioServer.js";
|
|
8
9
|
export { tapTransport } from "./tapTransport.js";
|
|
@@ -2597,3 +2597,78 @@ it("DELETE request to non-existent session returns 400", async () => {
|
|
|
2597
2597
|
await httpServer.close();
|
|
2598
2598
|
await stdioClient.close();
|
|
2599
2599
|
}, 15000);
|
|
2600
|
+
|
|
2601
|
+
// The SDK only writes a resumability "priming event" (an `id: <eventId>`
|
|
2602
|
+
// SSE line) when an event store is configured, so its presence/absence is a
|
|
2603
|
+
// reliable, wire-level signal of whether resumability is actually active -
|
|
2604
|
+
// see https://github.com/punkpeye/mcp-proxy/issues/72.
|
|
2605
|
+
const initializeAndGetRawBody = async (port: number) => {
|
|
2606
|
+
const response = await fetch(`http://localhost:${port}/mcp`, {
|
|
2607
|
+
body: JSON.stringify({
|
|
2608
|
+
id: 1,
|
|
2609
|
+
jsonrpc: "2.0",
|
|
2610
|
+
method: "initialize",
|
|
2611
|
+
params: {
|
|
2612
|
+
capabilities: {},
|
|
2613
|
+
clientInfo: { name: "test", version: "1.0.0" },
|
|
2614
|
+
// Priming events are only sent to clients whose negotiated protocol
|
|
2615
|
+
// version is >= 2025-11-25.
|
|
2616
|
+
protocolVersion: "2025-11-25",
|
|
2617
|
+
},
|
|
2618
|
+
}),
|
|
2619
|
+
headers: {
|
|
2620
|
+
Accept: "application/json, text/event-stream",
|
|
2621
|
+
"Content-Type": "application/json",
|
|
2622
|
+
},
|
|
2623
|
+
method: "POST",
|
|
2624
|
+
});
|
|
2625
|
+
|
|
2626
|
+
expect(response.status).toBe(200);
|
|
2627
|
+
|
|
2628
|
+
return response.text();
|
|
2629
|
+
};
|
|
2630
|
+
|
|
2631
|
+
it("disables the resumability event store when eventStore: false is passed", async () => {
|
|
2632
|
+
const port = await getRandomPort();
|
|
2633
|
+
|
|
2634
|
+
const httpServer = await startHTTPServer({
|
|
2635
|
+
createServer: async () => {
|
|
2636
|
+
return new Server(
|
|
2637
|
+
{ name: "test", version: "1.0.0" },
|
|
2638
|
+
{ capabilities: {} },
|
|
2639
|
+
);
|
|
2640
|
+
},
|
|
2641
|
+
eventStore: false,
|
|
2642
|
+
port,
|
|
2643
|
+
});
|
|
2644
|
+
|
|
2645
|
+
try {
|
|
2646
|
+
const body = await initializeAndGetRawBody(port);
|
|
2647
|
+
|
|
2648
|
+
expect(body).not.toMatch(/^id: /m);
|
|
2649
|
+
} finally {
|
|
2650
|
+
await httpServer.close();
|
|
2651
|
+
}
|
|
2652
|
+
});
|
|
2653
|
+
|
|
2654
|
+
it("enables a bounded, per-session resumability event store by default", async () => {
|
|
2655
|
+
const port = await getRandomPort();
|
|
2656
|
+
|
|
2657
|
+
const httpServer = await startHTTPServer({
|
|
2658
|
+
createServer: async () => {
|
|
2659
|
+
return new Server(
|
|
2660
|
+
{ name: "test", version: "1.0.0" },
|
|
2661
|
+
{ capabilities: {} },
|
|
2662
|
+
);
|
|
2663
|
+
},
|
|
2664
|
+
port,
|
|
2665
|
+
});
|
|
2666
|
+
|
|
2667
|
+
try {
|
|
2668
|
+
const body = await initializeAndGetRawBody(port);
|
|
2669
|
+
|
|
2670
|
+
expect(body).toMatch(/^id: /m);
|
|
2671
|
+
} finally {
|
|
2672
|
+
await httpServer.close();
|
|
2673
|
+
}
|
|
2674
|
+
});
|
package/src/startHTTPServer.ts
CHANGED
|
@@ -18,6 +18,25 @@ import { InMemoryEventStore } from "./InMemoryEventStore.js";
|
|
|
18
18
|
|
|
19
19
|
const DEFAULT_KEEP_ALIVE_TIMEOUT = 300_000;
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* `false` disables the resumability event store entirely (no replay-on-
|
|
23
|
+
* reconnect, no retained state). Omitted/`undefined` creates a fresh,
|
|
24
|
+
* bounded `InMemoryEventStore` per session - see `eventStoreMaxEvents`.
|
|
25
|
+
* Pass an `EventStore` instance to use a shared or custom-backed store.
|
|
26
|
+
*/
|
|
27
|
+
export type EventStoreOption = EventStore | false;
|
|
28
|
+
|
|
29
|
+
const resolveEventStore = (
|
|
30
|
+
eventStore: EventStoreOption | undefined,
|
|
31
|
+
maxEvents: number | undefined,
|
|
32
|
+
): EventStore | undefined => {
|
|
33
|
+
if (eventStore === false) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return eventStore ?? new InMemoryEventStore({ maxEvents });
|
|
38
|
+
};
|
|
39
|
+
|
|
21
40
|
export interface CorsOptions {
|
|
22
41
|
allowedHeaders?: string | string[]; // Allow string[] or '*' for wildcard
|
|
23
42
|
credentials?: boolean;
|
|
@@ -381,6 +400,7 @@ const handleStreamRequest = async <T extends ServerLike>({
|
|
|
381
400
|
enableJsonResponse,
|
|
382
401
|
endpoint,
|
|
383
402
|
eventStore,
|
|
403
|
+
eventStoreMaxEvents,
|
|
384
404
|
oauth,
|
|
385
405
|
onClose,
|
|
386
406
|
onConnect,
|
|
@@ -397,7 +417,8 @@ const handleStreamRequest = async <T extends ServerLike>({
|
|
|
397
417
|
createServer: (request: http.IncomingMessage) => Promise<T>;
|
|
398
418
|
enableJsonResponse?: boolean;
|
|
399
419
|
endpoint: string;
|
|
400
|
-
eventStore?:
|
|
420
|
+
eventStore?: EventStoreOption;
|
|
421
|
+
eventStoreMaxEvents?: number;
|
|
401
422
|
oauth?: AuthConfig["oauth"];
|
|
402
423
|
onClose?: (server: T) => Promise<void>;
|
|
403
424
|
onConnect?: (server: T) => Promise<void>;
|
|
@@ -543,7 +564,7 @@ const handleStreamRequest = async <T extends ServerLike>({
|
|
|
543
564
|
// Create a new transport for the session
|
|
544
565
|
transport = new StreamableHTTPServerTransport({
|
|
545
566
|
enableJsonResponse,
|
|
546
|
-
eventStore: eventStore
|
|
567
|
+
eventStore: resolveEventStore(eventStore, eventStoreMaxEvents),
|
|
547
568
|
onsessioninitialized: (_sessionId) => {
|
|
548
569
|
// add only when the id Session id is generated (skip in stateless mode)
|
|
549
570
|
if (!stateless && _sessionId) {
|
|
@@ -637,7 +658,7 @@ const handleStreamRequest = async <T extends ServerLike>({
|
|
|
637
658
|
// In stateless mode, handle non-initialize requests by creating a new transport
|
|
638
659
|
transport = new StreamableHTTPServerTransport({
|
|
639
660
|
enableJsonResponse,
|
|
640
|
-
eventStore: eventStore
|
|
661
|
+
eventStore: resolveEventStore(eventStore, eventStoreMaxEvents),
|
|
641
662
|
onsessioninitialized: () => {
|
|
642
663
|
// No session tracking in stateless mode
|
|
643
664
|
},
|
|
@@ -979,6 +1000,7 @@ export const startHTTPServer = async <T extends ServerLike>({
|
|
|
979
1000
|
createServer,
|
|
980
1001
|
enableJsonResponse,
|
|
981
1002
|
eventStore,
|
|
1003
|
+
eventStoreMaxEvents,
|
|
982
1004
|
host = "::",
|
|
983
1005
|
keepAliveTimeout = DEFAULT_KEEP_ALIVE_TIMEOUT,
|
|
984
1006
|
oauth,
|
|
@@ -998,7 +1020,22 @@ export const startHTTPServer = async <T extends ServerLike>({
|
|
|
998
1020
|
cors?: boolean | CorsOptions;
|
|
999
1021
|
createServer: (request: http.IncomingMessage) => Promise<T>;
|
|
1000
1022
|
enableJsonResponse?: boolean;
|
|
1001
|
-
|
|
1023
|
+
/**
|
|
1024
|
+
* Event store for the streamable HTTP transport's resumability support.
|
|
1025
|
+
* Pass `false` to disable resumability entirely (recommended for
|
|
1026
|
+
* request/response-only deployments that don't need replay-on-reconnect).
|
|
1027
|
+
* Omit to get a fresh, bounded `InMemoryEventStore` per session - see
|
|
1028
|
+
* `eventStoreMaxEvents`. Pass an `EventStore` instance to bring your own
|
|
1029
|
+
* (e.g. a persistent, cross-process store); it will be shared across all
|
|
1030
|
+
* sessions handled by this server.
|
|
1031
|
+
*/
|
|
1032
|
+
eventStore?: EventStoreOption;
|
|
1033
|
+
/**
|
|
1034
|
+
* Caps how many events the auto-created per-session `InMemoryEventStore`
|
|
1035
|
+
* retains (oldest evicted first) before it's overridden by an explicit
|
|
1036
|
+
* `eventStore`. Bounds memory for long-lived sessions. Default: 1000.
|
|
1037
|
+
*/
|
|
1038
|
+
eventStoreMaxEvents?: number;
|
|
1002
1039
|
host?: string;
|
|
1003
1040
|
keepAliveTimeout?: number;
|
|
1004
1041
|
oauth?: AuthConfig["oauth"];
|
|
@@ -1107,6 +1144,7 @@ export const startHTTPServer = async <T extends ServerLike>({
|
|
|
1107
1144
|
enableJsonResponse,
|
|
1108
1145
|
endpoint: streamEndpoint,
|
|
1109
1146
|
eventStore,
|
|
1147
|
+
eventStoreMaxEvents,
|
|
1110
1148
|
oauth,
|
|
1111
1149
|
onClose,
|
|
1112
1150
|
onConnect,
|