Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,9 @@ If you run into any issues, checkout our [troubleshooting guide](./docs/troubles
- **Network** (2 tools)
- [`get_network_request`](docs/tool-reference.md#get_network_request)
- [`list_network_requests`](docs/tool-reference.md#list_network_requests)
- **Debugging** (4 tools)
- **Debugging** (5 tools)
- [`evaluate_script`](docs/tool-reference.md#evaluate_script)
- [`get_console_message`](docs/tool-reference.md#get_console_message)
- [`list_console_messages`](docs/tool-reference.md#list_console_messages)
- [`take_screenshot`](docs/tool-reference.md#take_screenshot)
- [`take_snapshot`](docs/tool-reference.md#take_snapshot)
Expand Down
13 changes: 12 additions & 1 deletion docs/tool-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@
- **[Network](#network)** (2 tools)
- [`get_network_request`](#get_network_request)
- [`list_network_requests`](#list_network_requests)
- **[Debugging](#debugging)** (4 tools)
- **[Debugging](#debugging)** (5 tools)
- [`evaluate_script`](#evaluate_script)
- [`get_console_message`](#get_console_message)
- [`list_console_messages`](#list_console_messages)
- [`take_screenshot`](#take_screenshot)
- [`take_snapshot`](#take_snapshot)
Expand Down Expand Up @@ -296,6 +297,16 @@ so returned values have to JSON-serializable.

---

### `get_console_message`

**Description:** Gets a console message by its ID. You can get all messages by calling [`list_console_messages`](#list_console_messages).

**Parameters:**

- **msgid** (number) **(required)**: The msgid of a console message on the page from the listed console messages

---

### `list_console_messages`

**Description:** List all console messages for the currently selected page since the last navigation.
Expand Down
8 changes: 8 additions & 0 deletions src/McpContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,14 @@ export class McpContext implements Context {
return this.#consoleCollector.getData(page);
}

getConsoleMessageStableId(message: ConsoleMessage | Error): number {
return this.#consoleCollector.getIdForResource(message);
}

getConsoleMessageById(id: number): ConsoleMessage | Error {
return this.#consoleCollector.getById(this.getSelectedPage(), id);
}

async newPage(): Promise<Page> {
const page = await this.browser.newPage();
const pages = await this.createPagesSnapshot();
Expand Down
85 changes: 79 additions & 6 deletions src/McpResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
*/
import type {ConsoleMessage, ResourceType} from 'puppeteer-core';

import {formatConsoleEvent} from './formatters/consoleFormatter.js';
import {
formatConsoleEventShort,
formatConsoleEventVerbose,
} from './formatters/consoleFormatter.js';
import {
getFormattedHeaderValue,
getFormattedResponseBody,
Expand All @@ -31,16 +34,18 @@ interface NetworkRequestData {
}

export interface ConsoleMessageData {
type: string;
message: string;
args: string[];
consoleMessageStableId: number;
type?: string;
message?: string;
args?: string[];
}

export class McpResponse implements Response {
#includePages = false;
#includeSnapshot = false;
#includeVerboseSnapshot = false;
#attachedNetworkRequestData?: NetworkRequestData;
#attachedConsoleMessageData?: ConsoleMessageData;
#consoleMessagesData?: ConsoleMessageData[];
#textResponseLines: string[] = [];
#images: ImageContentData[] = [];
Expand Down Expand Up @@ -118,6 +123,12 @@ export class McpResponse implements Response {
};
}

attachConsoleMessage(id: number): void {
this.#attachedConsoleMessageData = {
consoleMessageStableId: id,
};
}

get includePages(): boolean {
return this.#includePages;
}
Expand Down Expand Up @@ -192,14 +203,62 @@ export class McpResponse implements Response {
}
}

if (this.#attachedConsoleMessageData?.consoleMessageStableId) {
const message = context.getConsoleMessageById(
this.#attachedConsoleMessageData.consoleMessageStableId,
);
const consoleMessageStableId =
this.#attachedConsoleMessageData.consoleMessageStableId;
let data: ConsoleMessageData;
if ('args' in message) {
const consoleMessage = message as ConsoleMessage;
data = {
consoleMessageStableId,
type: consoleMessage.type(),
message: consoleMessage.text(),
args: await Promise.all(
consoleMessage.args().map(async arg => {
const stringArg = await arg.jsonValue().catch(() => {
// Ignore errors.
});
return typeof stringArg === 'object'
? JSON.stringify(stringArg)
: String(stringArg);
}),
),
};
} else {
data = {
consoleMessageStableId,
type: 'error',
message: (message as Error).message,
args: [],
};
}
this.#attachedConsoleMessageData = data;
}

if (this.#consoleDataOptions?.include) {
const messages = context.getConsoleData();
let messages = context.getConsoleData();

if (this.#consoleDataOptions.types?.length) {
const normalizedTypes = new Set(this.#consoleDataOptions.types);
messages = messages.filter(message => {
if ('type' in message) {
return normalizedTypes.has(message.type());
}
return normalizedTypes.has('error');
});
}

this.#consoleMessagesData = await Promise.all(
messages.map(async (item): Promise<ConsoleMessageData> => {
const consoleMessageStableId =
context.getConsoleMessageStableId(item);
if ('args' in item) {
const consoleMessage = item as ConsoleMessage;
return {
consoleMessageStableId,
type: consoleMessage.type(),
message: consoleMessage.text(),
args: await Promise.all(
Expand All @@ -215,6 +274,7 @@ export class McpResponse implements Response {
};
}
return {
consoleMessageStableId,
type: 'error',
message: (item as Error).message,
args: [],
Expand Down Expand Up @@ -283,6 +343,7 @@ Call ${handleDialog.name} to handle it before continuing.`);
}

response.push(...this.#getIncludeNetworkRequestsData(context));
response.push(...this.#getAttachedConsoleMessageData());

if (this.#networkRequestsOptions?.include) {
let requests = context.getNetworkRequests();
Expand Down Expand Up @@ -329,7 +390,7 @@ Call ${handleDialog.name} to handle it before continuing.`);
);
response.push(...data.info);
response.push(
...data.items.map(message => formatConsoleEvent(message)),
...data.items.map(message => formatConsoleEventShort(message)),
);
} else {
response.push('<no console messages found>');
Expand Down Expand Up @@ -376,6 +437,18 @@ Call ${handleDialog.name} to handle it before continuing.`);
};
}

#getAttachedConsoleMessageData(): string[] {
const response: string[] = [];
const data = this.#attachedConsoleMessageData;
if (!data) {
return response;
}

response.push(`## Console Message ${data.consoleMessageStableId}`);
response.push(formatConsoleEventVerbose(data));
return response;
}

#getIncludeNetworkRequestsData(context: McpContext): string[] {
const response: string[] = [];
const url = this.#attachedNetworkRequestData?.networkRequestStableId;
Expand Down
56 changes: 39 additions & 17 deletions src/formatters/consoleFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,55 @@ const logLevels: Record<string, string> = {
assert: 'Assert',
};

export function formatConsoleEvent(msg: ConsoleMessageData): string {
const logLevel = logLevels[msg.type] ?? 'Log';
const text = msg.message;
// The short format for a console message, based on a previous format.
export function formatConsoleEventShort(msg: ConsoleMessageData): string {
const args = msg.args ? formatArgs(msg, false) : '';
return `msgid=${msg.consoleMessageStableId} [${msg.type}] ${msg.message}${args}`;
}

// The verbose format for a console message, including all details.
export function formatConsoleEventVerbose(msg: ConsoleMessageData): string {
const logLevel = msg.type ? (logLevels[msg.type] ?? 'Log') : 'Log';
let result = `${logLevel}> ${msg.message}`;

if (msg.args && msg.args.length > 0) {
result += formatArgs(msg, true);
}

result += `
ID: ${msg.consoleMessageStableId}`;
result += `
Type: ${msg.type}`;

const formattedArgs = formatArgs(msg.args, text);
return `${logLevel}> ${text} ${formattedArgs}`.trim();
return result;
}

// Only includes the first arg and indicates that there are more args
function formatArgs(args: string[], messageText: string): string {
if (args.length === 0) {
// If `includeAllArgs` is false, only includes the first arg and indicates that there are more args.
function formatArgs(
consoleData: ConsoleMessageData,
includeAllArgs = false,
): string {
if (!consoleData.args || consoleData.args.length === 0) {
return '';
}

let formattedArgs = '';
const firstArg = args[0];
// In the short format version, we only include the first arg.
const messageArgsToFormat = includeAllArgs
? consoleData.args
: [consoleData.args[0]];

if (firstArg !== messageText) {
formattedArgs +=
typeof firstArg === 'object'
? JSON.stringify(firstArg)
: String(firstArg);
for (const arg of messageArgsToFormat) {
if (arg !== consoleData.message) {
formattedArgs += ' ';
formattedArgs +=
typeof arg === 'object' ? JSON.stringify(arg) : String(arg);
}
}

if (args.length > 1) {
return `${formattedArgs} ...`;
if (!includeAllArgs && consoleData.args.length > 1) {
formattedArgs += ` ...`;
}

return formattedArgs;
return formattedArgs.length > 0 ? ` Args:${formattedArgs}` : '';
}
1 change: 1 addition & 0 deletions src/tools/ToolDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export interface Response {
setIncludeSnapshot(value: boolean, verbose?: boolean): void;
attachImage(value: ImageContentData): void;
attachNetworkRequest(reqid: number): void;
attachConsoleMessage(msgid: number): void;
}

/**
Expand Down
21 changes: 20 additions & 1 deletion src/tools/console.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const FILTERABLE_MESSAGE_TYPES: readonly [
'verbose',
];

export const consoleTool = defineTool({
export const listConsoleMessages = defineTool({
name: 'list_console_messages',
description:
'List all console messages for the currently selected page since the last navigation.',
Expand Down Expand Up @@ -76,3 +76,22 @@ export const consoleTool = defineTool({
});
},
});

export const getConsoleMessage = defineTool({
name: 'get_console_message',
description: `Gets a console message by its ID. You can get all messages by calling ${listConsoleMessages.name}.`,
annotations: {
category: ToolCategories.DEBUGGING,
readOnlyHint: true,
},
schema: {
msgid: zod
.number()
.describe(
'The msgid of a console message on the page from the listed console messages',
),
},
handler: async (request, response) => {
response.attachConsoleMessage(request.params.msgid);
},
});
Loading