diff --git a/src/McpResponse.ts b/src/McpResponse.ts index 0ec11b541..1b025f40a 100644 --- a/src/McpResponse.ts +++ b/src/McpResponse.ts @@ -626,20 +626,23 @@ Call ${handleDialog.name} to handle it before continuing.`); if (this.#consoleDataOptions?.include) { const messages = data.consoleMessages ?? []; + const groupedMessages = this.#groupConsoleMessages(messages); response.push('## Console messages'); - if (messages.length) { + if (groupedMessages.length) { const paginationData = this.#dataWithPagination( - messages, + groupedMessages, this.#consoleDataOptions.pagination, ); structuredContent.pagination = paginationData.pagination; response.push(...paginationData.info); response.push( - ...paginationData.items.map(message => message.toString()), + ...paginationData.items.map(message => + this.#formatGroupedConsoleMessage(message), + ), ); structuredContent.consoleMessages = paginationData.items.map(message => - message.toJSON(), + this.#toGroupedConsoleMessageJson(message), ); } else { response.push(''); @@ -663,6 +666,61 @@ Call ${handleDialog.name} to handle it before continuing.`); }; } + #groupConsoleMessages( + messages: Array, + ): Array<{ + key: string; + message: ConsoleFormatter | IssueFormatter; + count: number; + }> { + const groupedMessages: Array<{ + key: string; + message: ConsoleFormatter | IssueFormatter; + count: number; + }> = []; + + for (const message of messages) { + const key = message.toString().replace(/^msgid=\d+\s+/, ''); + const lastGroup = groupedMessages.at(-1); + if (lastGroup?.key === key) { + lastGroup.count += 1; + continue; + } + + groupedMessages.push({ + key, + message, + count: 1, + }); + } + + return groupedMessages; + } + + #formatGroupedConsoleMessage(groupedMessage: { + key: string; + message: ConsoleFormatter | IssueFormatter; + count: number; + }): string { + const text = groupedMessage.message.toString(); + if (groupedMessage.count > 1) { + return `${text} [${groupedMessage.count} times]`; + } + return text; + } + + #toGroupedConsoleMessageJson(groupedMessage: { + key: string; + message: ConsoleFormatter | IssueFormatter; + count: number; + }): object { + const json = groupedMessage.message.toJSON() as Record; + if (groupedMessage.count > 1) { + json.occurrences = groupedMessage.count; + } + return json; + } + #dataWithPagination(data: T[], pagination?: PaginationOptions) { const response = []; const paginationResult = paginate(data, pagination); diff --git a/tests/tools/console.test.ts b/tests/tools/console.test.ts index 64ed12dce..0ec90372d 100644 --- a/tests/tools/console.test.ts +++ b/tests/tools/console.test.ts @@ -66,6 +66,43 @@ describe('console', () => { }); }); + it('groups consecutive identical messages', async () => { + await withMcpContext(async (response, context) => { + const page = await context.newPage(); + await page.setContent(` + + `); + await listConsoleMessages.handler({params: {}}, response, context); + + const formattedResponse = await response.handle('test', context); + const textContent = getTextContent(formattedResponse.content[0]); + const messageLines = textContent + .split('\n') + .filter(line => line.startsWith('msgid=')); + + assert.deepStrictEqual(messageLines.length, 3); + assert.ok(messageLines[0]?.endsWith('[log] same (1 args) [2 times]')); + assert.ok(messageLines[1]?.endsWith('[log] different (1 args)')); + assert.ok(messageLines[2]?.endsWith('[log] same (1 args) [2 times]')); + + const groupedConsoleMessages = ( + formattedResponse.structuredContent as { + consoleMessages?: Array>; + } + ).consoleMessages; + assert.ok(groupedConsoleMessages); + assert.deepStrictEqual(groupedConsoleMessages.length, 3); + assert.deepStrictEqual(groupedConsoleMessages[0]?.occurrences, 2); + assert.deepStrictEqual(groupedConsoleMessages[2]?.occurrences, 2); + }); + }); + describe('issues', () => { it('lists issues', async () => { await withMcpContext(async (response, context) => {