mirror of
https://github.com/modelcontextprotocol/servers.git
synced 2026-04-17 23:53:24 +02:00
"Everything Server crashes when multiple clients reconnect" * In index.ts - added a variable to hold the initialize timeout - store the timeout in the oninitialized handler - clear the timeout in the cleanup callback * In roots.ts - In the catch block of syncRoots, log the error to the console via .error rather than attempting to send to the client because the most probable case here is that we don't have a connection.
91 lines
3.1 KiB
TypeScript
91 lines
3.1 KiB
TypeScript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
import {
|
|
Root,
|
|
RootsListChangedNotificationSchema,
|
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
|
|
// Track roots by session id
|
|
export const roots: Map<string | undefined, Root[]> = new Map<
|
|
string | undefined,
|
|
Root[]
|
|
>();
|
|
|
|
/**
|
|
* Get the latest the client roots list for the session.
|
|
*
|
|
* - Request and cache the roots list for the session if it has not been fetched before.
|
|
* - Return the cached roots list for the session if it exists.
|
|
*
|
|
* When requesting the roots list for a session, it also sets up a `roots/list_changed`
|
|
* notification handler. This ensures that updates are automatically fetched and handled
|
|
* in real-time.
|
|
*
|
|
* This function is idempotent. It should only request roots from the client once per session,
|
|
* returning the cached version thereafter.
|
|
*
|
|
* @param {McpServer} server - An instance of the MCP server used to communicate with the client.
|
|
* @param {string} [sessionId] - An optional session id used to associate the roots list with a specific client session.
|
|
*
|
|
* @throws {Error} In case of a failure to request the roots from the client, an error log message is sent.
|
|
*/
|
|
export const syncRoots = async (server: McpServer, sessionId?: string) => {
|
|
const clientCapabilities = server.server.getClientCapabilities() || {};
|
|
const clientSupportsRoots: boolean = clientCapabilities?.roots !== undefined;
|
|
|
|
// Fetch the roots list for this client
|
|
if (clientSupportsRoots) {
|
|
// Function to request the updated roots list from the client
|
|
const requestRoots = async () => {
|
|
try {
|
|
// Request the updated roots list from the client
|
|
const response = await server.server.listRoots();
|
|
if (response && "roots" in response) {
|
|
// Store the roots list for this client
|
|
roots.set(sessionId, response.roots);
|
|
|
|
// Notify the client of roots received
|
|
await server.sendLoggingMessage(
|
|
{
|
|
level: "info",
|
|
logger: "everything-server",
|
|
data: `Roots updated: ${response?.roots?.length} root(s) received from client`,
|
|
},
|
|
sessionId
|
|
);
|
|
} else {
|
|
await server.sendLoggingMessage(
|
|
{
|
|
level: "info",
|
|
logger: "everything-server",
|
|
data: "Client returned no roots set",
|
|
},
|
|
sessionId
|
|
);
|
|
}
|
|
} catch (error) {
|
|
console.error(
|
|
`Failed to request roots from client ${sessionId}: ${
|
|
error instanceof Error ? error.message : String(error)
|
|
}`
|
|
);
|
|
}
|
|
};
|
|
|
|
// If the roots have not been synced for this client,
|
|
// set notification handler and request initial roots
|
|
if (!roots.has(sessionId)) {
|
|
// Set the list changed notification handler
|
|
server.server.setNotificationHandler(
|
|
RootsListChangedNotificationSchema,
|
|
requestRoots
|
|
);
|
|
|
|
// Request the initial roots list immediately
|
|
await requestRoots();
|
|
}
|
|
|
|
// Return the roots list for this client
|
|
return roots.get(sessionId);
|
|
}
|
|
};
|