diff --git a/src/everything/resources/subscriptions.ts b/src/everything/resources/subscriptions.ts index 854a8633a2..7e7982a9b2 100644 --- a/src/everything/resources/subscriptions.ts +++ b/src/everything/resources/subscriptions.ts @@ -166,3 +166,22 @@ export const stopSimulatedResourceUpdates = (sessionId?: string) => { subsUpdateIntervals.delete(sessionId); } }; + +/** + * Removes a disconnected session from all subscription Sets. + * + * Called from the server factory's `cleanup()` when a transport closes so + * that a client that subscribed and then disconnects without unsubscribing + * does not leave its sessionId in every Set it joined for the life of the + * process. Drops empty entries to avoid unbounded map growth. + * + * @param sessionId - The session ID to remove (can be undefined for stdio). + */ +export const removeSubscriber = (sessionId?: string) => { + for (const [uri, subscribers] of subscriptions.entries()) { + subscribers.delete(sessionId); + if (subscribers.size === 0) { + subscriptions.delete(uri); + } + } +}; diff --git a/src/everything/server/index.ts b/src/everything/server/index.ts index f1459cc812..da130ce391 100644 --- a/src/everything/server/index.ts +++ b/src/everything/server/index.ts @@ -6,6 +6,7 @@ import { import { setSubscriptionHandlers, stopSimulatedResourceUpdates, + removeSubscriber, } from "../resources/subscriptions.js"; import { registerConditionalTools, registerTools } from "../tools/index.js"; import { registerResources, readInstructions } from "../resources/index.js"; @@ -110,6 +111,7 @@ export const createServer: () => ServerFactoryResponse = () => { // Stop any simulated logging or resource updates that may have been initiated. stopSimulatedLogging(sessionId); stopSimulatedResourceUpdates(sessionId); + removeSubscriber(sessionId); // Clean up task store timers taskStore.cleanup(); if (initializeTimeout) clearTimeout(initializeTimeout);