From dc60575f6c080250230e26b6a32849388642017e Mon Sep 17 00:00:00 2001 From: Harapan Rachman Date: Fri, 12 Jun 2026 11:14:03 +0700 Subject: [PATCH 1/2] UI : fix SSE transport detection and routing through CORS proxy. Assisted-by: Antigravity --- tools/ui/src/lib/services/mcp.service.ts | 56 ++++++++++++++++++++++++ tools/ui/src/lib/utils/mcp.ts | 18 ++++++-- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/tools/ui/src/lib/services/mcp.service.ts b/tools/ui/src/lib/services/mcp.service.ts index d596381aa05..4fa16edc517 100644 --- a/tools/ui/src/lib/services/mcp.service.ts +++ b/tools/ui/src/lib/services/mcp.service.ts @@ -236,6 +236,36 @@ export class MCPService { return { fetch: async (input, init) => { + if (useProxy && typeof window !== 'undefined') { + let requestUrlStr = ''; + if (typeof input === 'string') { + requestUrlStr = input; + } else if (input instanceof URL) { + requestUrlStr = input.href; + } + + if (requestUrlStr) { + const parsedRequestUrl = new URL(requestUrlStr, window.location.origin); + if ( + parsedRequestUrl.origin === window.location.origin && + !parsedRequestUrl.pathname.includes('/cors-proxy') + ) { + const originalConfigUrl = new URL(config.url); + const realTargetUrl = new URL( + parsedRequestUrl.pathname + parsedRequestUrl.search, + originalConfigUrl.origin + ); + const proxiedUrl = buildProxiedUrl(realTargetUrl.href); + + if (typeof input === 'string') { + input = proxiedUrl.href; + } else if (input instanceof URL) { + input = proxiedUrl; + } + } + } + } + const startedAt = performance.now(); const requestHeaders = new Headers(baseInit.headers); @@ -402,6 +432,32 @@ export class MCPService { stopPhaseLogging: () => {} }; } + + if (config.transport === MCPTransportType.SSE) { + const url = useProxy ? buildProxiedUrl(config.url) : new URL(config.url); + const { fetch: diagnosticFetch, disable: stopPhaseLogging } = this.createDiagnosticFetch( + serverName, + config, + requestInit, + url, + useProxy, + onLog + ); + + if (import.meta.env.DEV && import.meta.env.VITE_DEBUG) { + console.log(`[MCPService] Creating SSE transport for ${url.href}`); + } + + return { + transport: new SSEClientTransport(url, { + requestInit, + fetch: diagnosticFetch, + eventSourceInit: { fetch: diagnosticFetch } + }), + type: MCPTransportType.SSE, + stopPhaseLogging + }; + } const url = useProxy ? buildProxiedUrl(config.url) : new URL(config.url); const { fetch: diagnosticFetch, disable: stopPhaseLogging } = this.createDiagnosticFetch( diff --git a/tools/ui/src/lib/utils/mcp.ts b/tools/ui/src/lib/utils/mcp.ts index 05fe90048f0..a3d88e152fb 100644 --- a/tools/ui/src/lib/utils/mcp.ts +++ b/tools/ui/src/lib/utils/mcp.ts @@ -41,10 +41,22 @@ import type { MimeTypeUnion } from '$lib/types/common'; export function detectMcpTransportFromUrl(url: string): MCPTransportType { const normalized = url.trim().toLowerCase(); - return normalized.startsWith(UrlProtocol.WEBSOCKET) || + if ( + normalized.startsWith(UrlProtocol.WEBSOCKET) || normalized.startsWith(UrlProtocol.WEBSOCKET_SECURE) - ? MCPTransportType.WEBSOCKET - : MCPTransportType.STREAMABLE_HTTP; + ) { + return MCPTransportType.WEBSOCKET; + } + + if ( + normalized.endsWith('/sse') || + normalized.endsWith('/sse/') || + normalized.includes('/sse?') + ) { + return MCPTransportType.SSE; + } + + return MCPTransportType.STREAMABLE_HTTP; } /** From 794af403e2bee9385830f3414f72a5b5219ec223 Mon Sep 17 00:00:00 2001 From: Harapan Rachman Date: Mon, 15 Jun 2026 06:37:35 +0700 Subject: [PATCH 2/2] ui : replace magic strings with constants in MCP transport handling --- tools/ui/src/lib/constants/mcp.ts | 5 +++++ tools/ui/src/lib/services/mcp.service.ts | 7 ++++--- tools/ui/src/lib/utils/mcp.ts | 11 +++++++---- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/tools/ui/src/lib/constants/mcp.ts b/tools/ui/src/lib/constants/mcp.ts index 918eb9f94b7..5b11f989e20 100644 --- a/tools/ui/src/lib/constants/mcp.ts +++ b/tools/ui/src/lib/constants/mcp.ts @@ -84,3 +84,8 @@ export const MCP_TRANSPORT_ICONS: Record = { [MCPTransportType.STREAMABLE_HTTP]: Globe, [MCPTransportType.SSE]: Radio }; + +/** Standard SSE endpoint path indicators */ +export const MCP_SSE_ENDPOINT = '/sse'; +export const MCP_SSE_ENDPOINT_SLASH = '/sse/'; +export const MCP_SSE_ENDPOINT_QUERY = '/sse?'; diff --git a/tools/ui/src/lib/services/mcp.service.ts b/tools/ui/src/lib/services/mcp.service.ts index 4fa16edc517..0aa58dc5d80 100644 --- a/tools/ui/src/lib/services/mcp.service.ts +++ b/tools/ui/src/lib/services/mcp.service.ts @@ -16,7 +16,8 @@ import { DEFAULT_MCP_CONFIG, DEFAULT_CLIENT_VERSION, DEFAULT_IMAGE_MIME_TYPE, - MCP_PARTIAL_REDACT_HEADERS + MCP_PARTIAL_REDACT_HEADERS, + CORS_PROXY_ENDPOINT } from '$lib/constants'; import { MCPConnectionPhase, @@ -248,7 +249,7 @@ export class MCPService { const parsedRequestUrl = new URL(requestUrlStr, window.location.origin); if ( parsedRequestUrl.origin === window.location.origin && - !parsedRequestUrl.pathname.includes('/cors-proxy') + !parsedRequestUrl.pathname.includes(CORS_PROXY_ENDPOINT) ) { const originalConfigUrl = new URL(config.url); const realTargetUrl = new URL( @@ -432,7 +433,7 @@ export class MCPService { stopPhaseLogging: () => {} }; } - + if (config.transport === MCPTransportType.SSE) { const url = useProxy ? buildProxiedUrl(config.url) : new URL(config.url); const { fetch: diagnosticFetch, disable: stopPhaseLogging } = this.createDiagnosticFetch( diff --git a/tools/ui/src/lib/utils/mcp.ts b/tools/ui/src/lib/utils/mcp.ts index a3d88e152fb..7ce7f19fe98 100644 --- a/tools/ui/src/lib/utils/mcp.ts +++ b/tools/ui/src/lib/utils/mcp.ts @@ -19,7 +19,10 @@ import { DISPLAY_NAME_SEPARATOR_REGEX, PATH_SEPARATOR, RESOURCE_TEXT_CONTENT_SEPARATOR, - DEFAULT_RESOURCE_FILENAME + DEFAULT_RESOURCE_FILENAME, + MCP_SSE_ENDPOINT, + MCP_SSE_ENDPOINT_SLASH, + MCP_SSE_ENDPOINT_QUERY } from '$lib/constants'; import { Database, @@ -49,9 +52,9 @@ export function detectMcpTransportFromUrl(url: string): MCPTransportType { } if ( - normalized.endsWith('/sse') || - normalized.endsWith('/sse/') || - normalized.includes('/sse?') + normalized.endsWith(MCP_SSE_ENDPOINT) || + normalized.endsWith(MCP_SSE_ENDPOINT_SLASH) || + normalized.includes(MCP_SSE_ENDPOINT_QUERY) ) { return MCPTransportType.SSE; }