diff --git a/actions/setup/js/parse_copilot_log.cjs b/actions/setup/js/parse_copilot_log.cjs index 5daa74ea10..053b947924 100644 --- a/actions/setup/js/parse_copilot_log.cjs +++ b/actions/setup/js/parse_copilot_log.cjs @@ -40,6 +40,7 @@ function extractPremiumRequestCount(logContent) { */ function parseCopilotLog(logContent) { let logEntries; + let mcpFailures = []; // First, try to parse as JSON array (structured format) try { @@ -49,9 +50,10 @@ function parseCopilotLog(logContent) { } } catch (jsonArrayError) { // If that fails, try to parse as debug logs format - const debugLogEntries = parseDebugLogFormat(logContent); - if (debugLogEntries && debugLogEntries.length > 0) { - logEntries = debugLogEntries; + const debugLogResult = parseDebugLogFormat(logContent); + if (debugLogResult && debugLogResult.entries && debugLogResult.entries.length > 0) { + logEntries = debugLogResult.entries; + mcpFailures = debugLogResult.mcpFailures || []; } else { // Try JSONL format using shared function logEntries = parseLogEntries(logContent); @@ -126,7 +128,12 @@ function parseCopilotLog(logContent) { }, }); - return { markdown, logEntries }; + // Return result with mcpFailures if any were detected + const result = { markdown, logEntries }; + if (mcpFailures.length > 0) { + result.mcpFailures = mcpFailures; + } + return result; } /** @@ -208,15 +215,34 @@ function scanForToolErrors(logContent) { /** * Parses Copilot CLI debug log format and reconstructs the conversation flow * @param {string} logContent - Raw debug log content - * @returns {Array} Array of log entries in structured format + * @returns {{entries: Array, mcpFailures: string[]}} Object with array of log entries and array of failed MCP server names */ function parseDebugLogFormat(logContent) { const entries = []; const lines = logContent.split("\n"); + const mcpFailures = []; // First pass: scan for tool errors const toolErrors = scanForToolErrors(logContent); + // Scan for MCP server connection failures + // Pattern: [ERROR] Failed to start MCP client for remote server : TypeError: fetch failed + for (const line of lines) { + const mcpFailureMatch = line.match(/\[ERROR\]\s+Failed to start MCP client for remote server\s+([^:]+):\s+TypeError:\s+fetch failed/i); + if (mcpFailureMatch) { + const serverName = mcpFailureMatch[1].trim(); + if (serverName && !mcpFailures.includes(serverName)) { + mcpFailures.push(serverName); + } + } + } + + // Fail the step immediately if MCP server failures are detected + if (mcpFailures.length > 0) { + const failedServers = mcpFailures.join(", "); + core.setFailed(`MCP server(s) failed to launch: ${failedServers}`); + } + // Extract model information from the start let model = "unknown"; let sessionId = null; @@ -683,7 +709,7 @@ function parseDebugLogFormat(logContent) { } } - return entries; + return { entries, mcpFailures }; } // Export for testing diff --git a/actions/setup/js/parse_copilot_log.test.cjs b/actions/setup/js/parse_copilot_log.test.cjs index f3fcaa7dc6..66662eb685 100644 --- a/actions/setup/js/parse_copilot_log.test.cjs +++ b/actions/setup/js/parse_copilot_log.test.cjs @@ -278,4 +278,83 @@ describe("parse_copilot_log.cjs", () => { expect(result.markdown).toContain("safe_outputs::create_issue"); }); }); + + describe("MCP server failure detection", () => { + it("should detect MCP server connection failures from debug logs", () => { + const logWithMcpFailure = `2026-01-11T07:21:35.050Z [DEBUG] Starting Copilot CLI +2026-01-11T07:21:35.050Z [ERROR] Failed to start MCP client for remote server github: TypeError: fetch failed +2026-01-11T07:21:35.100Z [DEBUG] data: +2026-01-11T07:21:35.100Z [DEBUG] { +2026-01-11T07:21:35.100Z [DEBUG] "choices": [{"message": {"content": "test"}}] +2026-01-11T07:21:35.100Z [DEBUG] }`; + + const result = parseCopilotLog(logWithMcpFailure); + + expect(result.mcpFailures).toBeDefined(); + expect(result.mcpFailures).toHaveLength(1); + expect(result.mcpFailures[0]).toBe("github"); + expect(mockCore.setFailed).toHaveBeenCalledWith("MCP server(s) failed to launch: github"); + }); + + it("should detect multiple MCP server failures", () => { + const logWithMultipleMcpFailures = `2026-01-11T07:21:35.050Z [DEBUG] Starting Copilot CLI +2026-01-11T07:21:35.050Z [ERROR] Failed to start MCP client for remote server github: TypeError: fetch failed +2026-01-11T07:21:35.100Z [ERROR] Failed to start MCP client for remote server playwright: TypeError: fetch failed +2026-01-11T07:21:35.150Z [DEBUG] data: +2026-01-11T07:21:35.150Z [DEBUG] { +2026-01-11T07:21:35.150Z [DEBUG] "choices": [{"message": {"content": "test"}}] +2026-01-11T07:21:35.150Z [DEBUG] }`; + + const result = parseCopilotLog(logWithMultipleMcpFailures); + + expect(result.mcpFailures).toBeDefined(); + expect(result.mcpFailures).toHaveLength(2); + expect(result.mcpFailures).toContain("github"); + expect(result.mcpFailures).toContain("playwright"); + expect(mockCore.setFailed).toHaveBeenCalledWith("MCP server(s) failed to launch: github, playwright"); + }); + + it("should not report MCP failures for successful logs", () => { + const successfulLog = `2026-01-11T07:21:35.050Z [DEBUG] Starting Copilot CLI +2026-01-11T07:21:35.050Z [DEBUG] Connected to MCP server github +2026-01-11T07:21:35.100Z [DEBUG] data: +2026-01-11T07:21:35.100Z [DEBUG] { +2026-01-11T07:21:35.100Z [DEBUG] "choices": [{"message": {"content": "test"}}] +2026-01-11T07:21:35.100Z [DEBUG] }`; + + const result = parseCopilotLog(successfulLog); + + expect(result.mcpFailures).toBeUndefined(); + expect(mockCore.setFailed).not.toHaveBeenCalled(); + }); + + it("should handle JSON format without MCP failures", () => { + const jsonLog = JSON.stringify([ + { type: "system", subtype: "init", session_id: "test", tools: ["Bash"], model: "gpt-4" }, + { type: "result", num_turns: 1, usage: { input_tokens: 100, output_tokens: 50 } }, + ]); + + const result = parseCopilotLog(jsonLog); + + expect(result.mcpFailures).toBeUndefined(); + expect(mockCore.setFailed).not.toHaveBeenCalled(); + }); + + it("should avoid duplicate MCP server names in failures list", () => { + const logWithDuplicateErrors = `2026-01-11T07:21:35.050Z [DEBUG] Starting Copilot CLI +2026-01-11T07:21:35.050Z [ERROR] Failed to start MCP client for remote server github: TypeError: fetch failed +2026-01-11T07:21:35.100Z [ERROR] Failed to start MCP client for remote server github: TypeError: fetch failed +2026-01-11T07:21:35.150Z [DEBUG] data: +2026-01-11T07:21:35.150Z [DEBUG] { +2026-01-11T07:21:35.150Z [DEBUG] "choices": [{"message": {"content": "test"}}] +2026-01-11T07:21:35.150Z [DEBUG] }`; + + const result = parseCopilotLog(logWithDuplicateErrors); + + expect(result.mcpFailures).toBeDefined(); + expect(result.mcpFailures).toHaveLength(1); + expect(result.mcpFailures[0]).toBe("github"); + expect(mockCore.setFailed).toHaveBeenCalledWith("MCP server(s) failed to launch: github"); + }); + }); });