@modelcontextprotocol/server-everything
Version:
MCP server that exercises all the features of the MCP protocol
45 lines (44 loc) • 1.85 kB
JavaScript
// The Streamable HTTP transport's event store, which makes its SSE streams
// resumable: a client that reconnects with a `Last-Event-ID` is sent the
// events it missed (#4087).
//
// Events are kept per stream, and a replay sends only the later events of the
// stream the given event belongs to, never another stream's. An unknown event
// id has no stream: `getStreamIdForEventId` reports that as `undefined`, which
// is how the SDK learns to refuse the resume before it calls
// `replayEventsAfter`. In memory and unbounded, so for examples and testing,
// not production. Split out of `streamableHttp.ts` (from #4099) so it can be
// tested directly.
import { randomUUID } from "node:crypto";
export class InMemoryEventStore {
events = new Map();
async storeEvent(streamId, message) {
const eventId = randomUUID();
this.events.set(eventId, { streamId, message });
return eventId;
}
async getStreamIdForEventId(eventId) {
return this.events.get(eventId)?.streamId;
}
async replayEventsAfter(lastEventId, { send, }) {
const lastEvent = this.events.get(lastEventId);
if (!lastEvent) {
// The SDK asks getStreamIdForEventId first and refuses an unknown id, so
// reaching here is a caller error: there is no stream to return.
throw new Error(`Unknown event ID: ${lastEventId}`);
}
const { streamId } = lastEvent;
let foundLastEvent = false;
for (const [eventId, event] of this.events) {
if (eventId === lastEventId) {
foundLastEvent = true;
continue;
}
if (!foundLastEvent || event.streamId !== streamId) {
continue;
}
await send(eventId, event.message);
}
return streamId;
}
}