From bc5709267cb42025a42fad44c089d7929add5040 Mon Sep 17 00:00:00 2001 From: Yu Peng <101682505+yupeng0512@users.noreply.github.com> Date: Sat, 3 Jan 2026 11:05:38 +0800 Subject: [PATCH] fix(mcp): prevent stuck spinner by initializing blank session state (#494) * fix(mcp): initialize blank state to avoid stuck spinner * style: fix formatting --------- Co-authored-by: dayuan.jiang --- packages/mcp-server/src/http-server.ts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/mcp-server/src/http-server.ts b/packages/mcp-server/src/http-server.ts index ef92208..e3e2031 100644 --- a/packages/mcp-server/src/http-server.ts +++ b/packages/mcp-server/src/http-server.ts @@ -29,12 +29,29 @@ function getOrigin(url: string): string { const DRAWIO_ORIGIN = getOrigin(DRAWIO_BASE_URL) +// Minimal blank diagram used to bootstrap new sessions. +// This avoids the draw.io embed spinner (spin=1) getting stuck when no `load(xml)` is ever sent. +const DEFAULT_DIAGRAM_XML = `` + // Normalize URL for iframe src - ensure no double slashes function normalizeUrl(url: string): string { // Remove trailing slash to avoid double slashes return url.replace(/\/$/, "") } +function isLikelyMcpSessionId(sessionId: string): boolean { + // Keep this cheap and conservative to avoid creating state for arbitrary IDs. + return sessionId.startsWith("mcp-") && sessionId.length <= 128 +} + +function ensureSessionStateInitialized(sessionId: string): void { + if (!sessionId) return + if (!isLikelyMcpSessionId(sessionId)) return + if (stateStore.has(sessionId)) return + + setState(sessionId, DEFAULT_DIAGRAM_XML) +} + interface SessionState { xml: string version: number @@ -177,8 +194,11 @@ function handleRequest( } if (url.pathname === "/" || url.pathname === "/index.html") { + const sessionId = url.searchParams.get("mcp") || "" + ensureSessionStateInitialized(sessionId) + res.writeHead(200, { "Content-Type": "text/html" }) - res.end(getHtmlPage(url.searchParams.get("mcp") || "")) + res.end(getHtmlPage(sessionId)) } else if (url.pathname === "/api/state") { handleStateApi(req, res, url) } else if (url.pathname === "/api/history") { @@ -205,6 +225,7 @@ function handleStateApi( res.end(JSON.stringify({ error: "sessionId required" })) return } + ensureSessionStateInitialized(sessionId) const state = stateStore.get(sessionId) res.writeHead(200, { "Content-Type": "application/json" }) res.end(