From 02aacb46a2851cf6c5066659f1bc3b56a44d3c76 Mon Sep 17 00:00:00 2001 From: cliffhall Date: Thu, 29 May 2025 19:54:36 -0400 Subject: [PATCH] Send HTTP/DELETE when disconnecting client * In server/src/index.ts - add delete handler for /mcp endpoint - gets the server transport for the sessionId - calls terminateSession on it - removes the webapp and server transports for the sessionId from the maps - returns status 200 to the client. * In client/src/lib/hooks/useConnection.ts - import Transport - add useState for clientTransport, type Transport or null initialized to null - in connect() function - move creation of client transport down a ways, just before calling client.connect with it - immendiately efter calling client.connect, call setClientTransport with the connected transport (has to happen after, so that what's saved has the abort controller, etc. otherwise it doesn't work to save it prior. - in disconnect() function - immediately call clientTransport.terminateSession if transportType is "streamable-http" - setClientTransport to null --- client/src/lib/hooks/useConnection.ts | 32 +++++++++++++++++++-------- server/src/index.ts | 27 ++++++++++++++++++++++ 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/client/src/lib/hooks/useConnection.ts b/client/src/lib/hooks/useConnection.ts index 9c8253189..39da55cbb 100644 --- a/client/src/lib/hooks/useConnection.ts +++ b/client/src/lib/hooks/useConnection.ts @@ -45,6 +45,7 @@ import { } from "@/utils/configUtils"; import { getMCPServerRequestTimeout } from "@/utils/configUtils"; import { InspectorConfig } from "../configurationTypes"; +import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; interface UseConnectionOptions { transportType: "stdio" | "sse" | "streamable-http"; @@ -83,6 +84,9 @@ export function useConnection({ const [serverCapabilities, setServerCapabilities] = useState(null); const [mcpClient, setMcpClient] = useState(null); + const [clientTransport, setClientTransport] = useState( + null, + ); const [requestHistory, setRequestHistory] = useState< { request: string; response?: string }[] >([]); @@ -377,14 +381,6 @@ export function useConnection({ transportType, ); - const clientTransport = - transportType === "streamable-http" - ? new StreamableHTTPClientTransport(mcpProxyServerUrl as URL, { - sessionId: undefined, - ...transportOptions, - }) - : new SSEClientTransport(mcpProxyServerUrl as URL, transportOptions); - if (onNotification) { [ CancelledNotificationSchema, @@ -414,7 +410,20 @@ export function useConnection({ let capabilities; try { - await client.connect(clientTransport); + const transport = + transportType === "streamable-http" + ? new StreamableHTTPClientTransport(mcpProxyServerUrl as URL, { + sessionId: undefined, + ...transportOptions, + }) + : new SSEClientTransport( + mcpProxyServerUrl as URL, + transportOptions, + ); + + await client.connect(transport as Transport); + + setClientTransport(transport); capabilities = client.getServerCapabilities(); const initializeRequest = { @@ -468,10 +477,15 @@ export function useConnection({ }; const disconnect = async () => { + if (transportType === "streamable-http") + await ( + clientTransport as StreamableHTTPClientTransport + ).terminateSession(); await mcpClient?.close(); const authProvider = new InspectorOAuthClientProvider(sseUrl); authProvider.clear(); setMcpClient(null); + setClientTransport(null); setConnectionStatus("disconnected"); setCompletionsSupported(false); setServerCapabilities(null); diff --git a/server/src/index.ts b/server/src/index.ts index 73288f672..1586d0a0f 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -226,6 +226,33 @@ app.post("/mcp", async (req, res) => { } }); +app.delete("/mcp", async (req, res) => { + const sessionId = req.headers["mcp-session-id"] as string | undefined; + console.log(`Received DELETE message for sessionId ${sessionId}`); + let serverTransport: Transport | undefined; + if (sessionId) { + try { + serverTransport = serverTransports.get( + sessionId, + ) as StreamableHTTPClientTransport; + if (!serverTransport) { + res.status(404).end("Transport not found for sessionId " + sessionId); + } else { + await ( + serverTransport as StreamableHTTPClientTransport + ).terminateSession(); + webAppTransports.delete(sessionId); + serverTransports.delete(sessionId); + console.log(`Transports removed for sessionId ${sessionId}`); + } + res.status(200).end(); + } catch (error) { + console.error("Error in /mcp route:", error); + res.status(500).json(error); + } + } +}); + app.get("/stdio", async (req, res) => { try { console.log("New connection");