diff --git a/tools/ui/src/lib/services/mcp.service.ts b/tools/ui/src/lib/services/mcp.service.ts index 4432989171..ae98632a6f 100644 --- a/tools/ui/src/lib/services/mcp.service.ts +++ b/tools/ui/src/lib/services/mcp.service.ts @@ -314,6 +314,30 @@ export class MCPService { ) ); + if (method === 'DELETE' && url.includes(CORS_PROXY_ENDPOINT)) { + const response = new Response(null, { status: 200, statusText: 'OK' }); + + logIfEnabled( + this.createLog( + MCPConnectionPhase.INITIALIZING, + `HTTP 200 ${method} ${url} (fake response)`, + MCPLogLevel.INFO, + { + response: { + url, + status: response.status, + statusText: response.statusText, + durationMs: 0, + isFake: true + } + } + ) + ); + + // fake response, bypass real fetch() + return response; + } + try { const response = await fetch(input, { ...baseInit, diff --git a/tools/ui/tests/unit/mcp-service.test.ts b/tools/ui/tests/unit/mcp-service.test.ts index 1f6fdda377..d3af906813 100644 --- a/tools/ui/tests/unit/mcp-service.test.ts +++ b/tools/ui/tests/unit/mcp-service.test.ts @@ -154,6 +154,32 @@ describe('MCPService', () => { }); }); + it('DELETE request with CORS proxy should return a fake 200 response', async () => { + const logs: MCPConnectionLog[] = []; + const fetchMock = vi.fn(); + + vi.stubGlobal('fetch', fetchMock); + + const config: MCPServerConfig = { + url: 'https://example.com/mcp', + transport: MCPTransportType.STREAMABLE_HTTP, + useProxy: true + }; + + const controller = createDiagnosticFetch(config, (log) => logs.push(log), {}, true); + + const response = await controller.fetch( + 'http://localhost:8080/cors-proxy?url=https%3A%2F%2Fexample.com%2Fmcp', + { method: 'DELETE' } + ); + + expect(fetchMock).not.toHaveBeenCalled(); + expect(response.status).toBe(200); + expect(logs.at(-1)?.details).toMatchObject({ + response: { status: 200, isFake: true } + }); + }); + it('partially redacts mcp-session-id in diagnostic request and response logs', async () => { const logs: MCPConnectionLog[] = []; const response = new Response('{}', {