From c15cf5947ac718d19e4ee3cb8480af58e06bf4a7 Mon Sep 17 00:00:00 2001 From: "Tommy D. Rossi" Date: Wed, 31 Dec 2025 22:41:32 +0100 Subject: [PATCH] Add unified mcpLog for relay server and stderr logging --- playwriter/src/mcp.ts | 34 +++++++++++++++--------- website/public/resources/debugger-api.md | 7 ++--- website/public/resources/editor-api.md | 4 +-- website/public/resources/styles-api.md | 6 ++--- 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/playwriter/src/mcp.ts b/playwriter/src/mcp.ts index c0f4657..0978676 100644 --- a/playwriter/src/mcp.ts +++ b/playwriter/src/mcp.ts @@ -189,6 +189,15 @@ async function sendLogToRelayServer(level: string, ...args: any[]) { } } +/** + * Log to both console.error (for early startup) and relay server log file. + * Fire-and-forget to avoid blocking. + */ +function mcpLog(...args: any[]) { + console.error(...args) + sendLogToRelayServer('log', ...args) +} + async function getServerVersion(port: number): Promise { try { const response = await fetch(`http://127.0.0.1:${port}/version`, { @@ -246,10 +255,10 @@ async function ensureRelayServer(): Promise { } if (serverVersion !== null) { - console.error(`CDP relay server version mismatch (server: ${serverVersion}, mcp: ${VERSION}), restarting...`) + mcpLog(`CDP relay server version mismatch (server: ${serverVersion}, mcp: ${VERSION}), restarting...`) await killRelayServer(RELAY_PORT) } else { - console.error('CDP relay server not running, starting it...') + mcpLog('CDP relay server not running, starting it...') } const scriptPath = require.resolve('../dist/start-relay-server.js') @@ -265,7 +274,7 @@ async function ensureRelayServer(): Promise { await sleep(500) const newVersion = await getServerVersion(RELAY_PORT) if (newVersion === VERSION) { - console.error('CDP relay server started successfully, waiting for extension to connect...') + mcpLog('CDP relay server started successfully, waiting for extension to connect...') await sleep(1000) return } @@ -370,7 +379,7 @@ function setupPageConsoleListener(page: Page) { pageLogs.shift() } } catch (e) { - console.error('[MCP] Failed to get console message text:', e) + mcpLog('[MCP] Failed to get console message text:', e) return } }) @@ -402,7 +411,7 @@ async function resetConnection(): Promise<{ browser: Browser; page: Page; contex try { await state.browser.close() } catch (e) { - console.error('Error closing browser:', e) + mcpLog('Error closing browser:', e) } } @@ -547,7 +556,7 @@ server.tool( const page = await getCurrentPage(timeout) const context = state.context || page.context() - console.error('Executing code:', code) + mcpLog('Executing code:', code) const customConsole = { log: (...args: any[]) => { @@ -813,15 +822,16 @@ server.tool( } } catch (error: any) { const errorStack = error.stack || error.message + const isTimeoutError = error instanceof CodeExecutionTimeoutError || error.name === 'TimeoutError' + + // Always log to stderr, but only send non-timeout errors to relay server console.error('Error in execute tool:', errorStack) + if (!isTimeoutError) { + sendLogToRelayServer('error', 'Error in execute tool:', errorStack) + } const logsText = formatConsoleLogs(consoleLogs, 'Console output (before error)') - const isTimeoutError = error instanceof CodeExecutionTimeoutError || error.name === 'TimeoutError' - if (!isTimeoutError) { - sendLogToRelayServer('error', '[MCP] Error:', errorStack) - } - const resetHint = isTimeoutError ? '' : '\n\n[HINT: If this is an internal Playwright error, page/browser closed, or connection issue, call the `reset` tool to reconnect. Do NOT reset for other non-connection non-internal errors.]' @@ -908,7 +918,7 @@ export async function startMcp(options: { host?: string; token?: string } = {}) if (!remote) { await ensureRelayServer() } else { - console.error(`Using remote CDP relay server: ${remote.host}:${remote.port}`) + mcpLog(`Using remote CDP relay server: ${remote.host}:${remote.port}`) await checkRemoteServer(remote) } const transport = new StdioServerTransport() diff --git a/website/public/resources/debugger-api.md b/website/public/resources/debugger-api.md index 0ff1315..e0df817 100644 --- a/website/public/resources/debugger-api.md +++ b/website/public/resources/debugger-api.md @@ -3,7 +3,7 @@ ## Types ```ts -import type { CDPSession } from './cdp-session.js'; +import type { ICDPSession } from './cdp-session.js'; export interface BreakpointInfo { id: string; file: string; @@ -57,7 +57,8 @@ export declare class Debugger { * Creates a new Debugger instance. * * @param options - Configuration options - * @param options.cdp - A CDPSession instance for sending CDP commands + * @param options.cdp - A CDPSession instance for sending CDP commands (works with both + * our CDPSession and Playwright's CDPSession) * * @example * ```ts @@ -66,7 +67,7 @@ export declare class Debugger { * ``` */ constructor({ cdp }: { - cdp: CDPSession; + cdp: ICDPSession; }); private setupEventListeners; /** diff --git a/website/public/resources/editor-api.md b/website/public/resources/editor-api.md index 043227f..03da78b 100644 --- a/website/public/resources/editor-api.md +++ b/website/public/resources/editor-api.md @@ -5,7 +5,7 @@ The Editor class provides a Claude Code-like interface for viewing and editing w ## Types ```ts -import type { CDPSession } from './cdp-session.js'; +import type { ICDPSession } from './cdp-session.js'; export interface ReadResult { content: string; totalLines: number; @@ -55,7 +55,7 @@ export declare class Editor { private stylesheets; private sourceCache; constructor({ cdp }: { - cdp: CDPSession; + cdp: ICDPSession; }); private setupEventListeners; /** diff --git a/website/public/resources/styles-api.md b/website/public/resources/styles-api.md index 5acf7ce..2d4b350 100644 --- a/website/public/resources/styles-api.md +++ b/website/public/resources/styles-api.md @@ -5,7 +5,7 @@ The getStylesForLocator function inspects CSS styles applied to an element, simi ## Types ```ts -import type { CDPSession } from './cdp-session.js'; +import type { ICDPSession } from './cdp-session.js'; import type { Locator } from 'playwright-core'; export interface StyleSource { url: string; @@ -25,9 +25,9 @@ export interface StylesResult { inlineStyle: StyleDeclarations | null; rules: StyleRule[]; } -export declare function getStylesForLocator({ locator, cdp, includeUserAgentStyles, }: { +export declare function getStylesForLocator({ locator, cdp: cdpSession, includeUserAgentStyles, }: { locator: Locator; - cdp: CDPSession; + cdp: ICDPSession; includeUserAgentStyles?: boolean; }): Promise; export declare function formatStylesAsText(styles: StylesResult): string;