diff --git a/src/everything/README.md b/src/everything/README.md index 8109e4449f..283c975615 100644 --- a/src/everything/README.md +++ b/src/everything/README.md @@ -104,3 +104,32 @@ npx @modelcontextprotocol/server-everything sse npx @modelcontextprotocol/server-everything streamableHttp ``` +## Environment Variables + +### DISABLE_SSE_RESUMABILITY + +Set to `true` to disable SSE resumability (eventStore). This prevents the server from sending the priming event (empty SSE event with an ID) that is required by [SEP-1699](https://modelcontextprotocol.io/community/seps/1699-support-sse-polling-via-server-side-disconnect) for protocol version 2025-11-25+. + +**Note**: This is a workaround for clients that don't properly handle empty SSE data events as specified by the [SSE standard](https://html.spec.whatwg.org/multipage/server-sent-events.html). The proper fix is to update the client to handle empty SSE events correctly. + +When resumability is disabled: +- SSE streams cannot be resumed after network failures +- Server cannot use polling mode (must keep connections open) +- Event replay is not available + +```shell +# Disable SSE resumability +DISABLE_SSE_RESUMABILITY=true npx @modelcontextprotocol/server-everything streamableHttp + +# Or with Docker +docker run -e DISABLE_SSE_RESUMABILITY=true -p 3001:3001 mcp/everything streamableHttp +``` + +### PORT + +Set the port for the HTTP servers (SSE and Streamable HTTP). Default: `3001` + +```shell +PORT=8080 npx @modelcontextprotocol/server-everything streamableHttp +``` + diff --git a/src/everything/transports/streamableHttp.ts b/src/everything/transports/streamableHttp.ts index 2e79abc554..ab5967a5f0 100644 --- a/src/everything/transports/streamableHttp.ts +++ b/src/everything/transports/streamableHttp.ts @@ -38,6 +38,13 @@ class InMemoryEventStore implements EventStore { console.log("Starting Streamable HTTP server..."); +// Check if SSE resumability should be disabled (workaround for clients that don't handle empty SSE events) +const DISABLE_SSE_RESUMABILITY = process.env.DISABLE_SSE_RESUMABILITY === 'true'; +if (DISABLE_SSE_RESUMABILITY) { + console.log("SSE resumability is DISABLED - eventStore will not be used"); + console.log("This is a workaround for clients that don't properly handle empty SSE data events"); +} + // Express app with permissive CORS for testing with Inspector direct connect mode const app = express(); app.use( @@ -72,10 +79,13 @@ app.post("/mcp", async (req: Request, res: Response) => { const { server, cleanup } = createServer(); // New initialization request - const eventStore = new InMemoryEventStore(); + // Only enable eventStore if resumability is not disabled + // Note: Disabling resumability is a workaround for clients that don't properly + // handle empty SSE data events (required by SEP-1699 for protocol >= 2025-11-25) + const eventStore = DISABLE_SSE_RESUMABILITY ? undefined : new InMemoryEventStore(); transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), - eventStore, // Enable resumability + eventStore, // Enable resumability (unless disabled) onsessioninitialized: (sessionId: string) => { // Store the transport by session ID when a session is initialized // This avoids race conditions where requests might come in before the session is stored