UI : fix SSE transport detection and routing through CORS proxy. Assi… (#24500)
* UI : fix SSE transport detection and routing through CORS proxy. Assisted-by: Antigravity * ui : replace magic strings with constants in MCP transport handling
This commit is contained in:
@@ -84,3 +84,8 @@ export const MCP_TRANSPORT_ICONS: Record<MCPTransportType, Component> = {
|
|||||||
[MCPTransportType.STREAMABLE_HTTP]: Globe,
|
[MCPTransportType.STREAMABLE_HTTP]: Globe,
|
||||||
[MCPTransportType.SSE]: Radio
|
[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?';
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ import {
|
|||||||
DEFAULT_MCP_CONFIG,
|
DEFAULT_MCP_CONFIG,
|
||||||
DEFAULT_CLIENT_VERSION,
|
DEFAULT_CLIENT_VERSION,
|
||||||
DEFAULT_IMAGE_MIME_TYPE,
|
DEFAULT_IMAGE_MIME_TYPE,
|
||||||
MCP_PARTIAL_REDACT_HEADERS
|
MCP_PARTIAL_REDACT_HEADERS,
|
||||||
|
CORS_PROXY_ENDPOINT
|
||||||
} from '$lib/constants';
|
} from '$lib/constants';
|
||||||
import {
|
import {
|
||||||
MCPConnectionPhase,
|
MCPConnectionPhase,
|
||||||
@@ -236,6 +237,36 @@ export class MCPService {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
fetch: async (input, init) => {
|
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_ENDPOINT)
|
||||||
|
) {
|
||||||
|
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 startedAt = performance.now();
|
||||||
const requestHeaders = new Headers(baseInit.headers);
|
const requestHeaders = new Headers(baseInit.headers);
|
||||||
|
|
||||||
@@ -403,6 +434,32 @@ export class MCPService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 url = useProxy ? buildProxiedUrl(config.url) : new URL(config.url);
|
||||||
const { fetch: diagnosticFetch, disable: stopPhaseLogging } = this.createDiagnosticFetch(
|
const { fetch: diagnosticFetch, disable: stopPhaseLogging } = this.createDiagnosticFetch(
|
||||||
serverName,
|
serverName,
|
||||||
|
|||||||
@@ -19,7 +19,10 @@ import {
|
|||||||
DISPLAY_NAME_SEPARATOR_REGEX,
|
DISPLAY_NAME_SEPARATOR_REGEX,
|
||||||
PATH_SEPARATOR,
|
PATH_SEPARATOR,
|
||||||
RESOURCE_TEXT_CONTENT_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';
|
} from '$lib/constants';
|
||||||
import {
|
import {
|
||||||
Database,
|
Database,
|
||||||
@@ -41,10 +44,22 @@ import type { MimeTypeUnion } from '$lib/types/common';
|
|||||||
export function detectMcpTransportFromUrl(url: string): MCPTransportType {
|
export function detectMcpTransportFromUrl(url: string): MCPTransportType {
|
||||||
const normalized = url.trim().toLowerCase();
|
const normalized = url.trim().toLowerCase();
|
||||||
|
|
||||||
return normalized.startsWith(UrlProtocol.WEBSOCKET) ||
|
if (
|
||||||
|
normalized.startsWith(UrlProtocol.WEBSOCKET) ||
|
||||||
normalized.startsWith(UrlProtocol.WEBSOCKET_SECURE)
|
normalized.startsWith(UrlProtocol.WEBSOCKET_SECURE)
|
||||||
? MCPTransportType.WEBSOCKET
|
) {
|
||||||
: MCPTransportType.STREAMABLE_HTTP;
|
return MCPTransportType.WEBSOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
normalized.endsWith(MCP_SSE_ENDPOINT) ||
|
||||||
|
normalized.endsWith(MCP_SSE_ENDPOINT_SLASH) ||
|
||||||
|
normalized.includes(MCP_SSE_ENDPOINT_QUERY)
|
||||||
|
) {
|
||||||
|
return MCPTransportType.SSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MCPTransportType.STREAMABLE_HTTP;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user