Add unified mcpLog for relay server and stderr logging

This commit is contained in:
Tommy D. Rossi
2025-12-31 22:41:32 +01:00
parent 240ce5be74
commit c15cf5947a
4 changed files with 31 additions and 20 deletions
+22 -12
View File
@@ -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<string | null> { async function getServerVersion(port: number): Promise<string | null> {
try { try {
const response = await fetch(`http://127.0.0.1:${port}/version`, { const response = await fetch(`http://127.0.0.1:${port}/version`, {
@@ -246,10 +255,10 @@ async function ensureRelayServer(): Promise<void> {
} }
if (serverVersion !== null) { 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) await killRelayServer(RELAY_PORT)
} else { } 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') const scriptPath = require.resolve('../dist/start-relay-server.js')
@@ -265,7 +274,7 @@ async function ensureRelayServer(): Promise<void> {
await sleep(500) await sleep(500)
const newVersion = await getServerVersion(RELAY_PORT) const newVersion = await getServerVersion(RELAY_PORT)
if (newVersion === VERSION) { 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) await sleep(1000)
return return
} }
@@ -370,7 +379,7 @@ function setupPageConsoleListener(page: Page) {
pageLogs.shift() pageLogs.shift()
} }
} catch (e) { } catch (e) {
console.error('[MCP] Failed to get console message text:', e) mcpLog('[MCP] Failed to get console message text:', e)
return return
} }
}) })
@@ -402,7 +411,7 @@ async function resetConnection(): Promise<{ browser: Browser; page: Page; contex
try { try {
await state.browser.close() await state.browser.close()
} catch (e) { } 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 page = await getCurrentPage(timeout)
const context = state.context || page.context() const context = state.context || page.context()
console.error('Executing code:', code) mcpLog('Executing code:', code)
const customConsole = { const customConsole = {
log: (...args: any[]) => { log: (...args: any[]) => {
@@ -813,15 +822,16 @@ server.tool(
} }
} catch (error: any) { } catch (error: any) {
const errorStack = error.stack || error.message 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) 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 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 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.]' : '\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) { if (!remote) {
await ensureRelayServer() await ensureRelayServer()
} else { } 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) await checkRemoteServer(remote)
} }
const transport = new StdioServerTransport() const transport = new StdioServerTransport()
+4 -3
View File
@@ -3,7 +3,7 @@
## Types ## Types
```ts ```ts
import type { CDPSession } from './cdp-session.js'; import type { ICDPSession } from './cdp-session.js';
export interface BreakpointInfo { export interface BreakpointInfo {
id: string; id: string;
file: string; file: string;
@@ -57,7 +57,8 @@ export declare class Debugger {
* Creates a new Debugger instance. * Creates a new Debugger instance.
* *
* @param options - Configuration options * @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 * @example
* ```ts * ```ts
@@ -66,7 +67,7 @@ export declare class Debugger {
* ``` * ```
*/ */
constructor({ cdp }: { constructor({ cdp }: {
cdp: CDPSession; cdp: ICDPSession;
}); });
private setupEventListeners; private setupEventListeners;
/** /**
+2 -2
View File
@@ -5,7 +5,7 @@ The Editor class provides a Claude Code-like interface for viewing and editing w
## Types ## Types
```ts ```ts
import type { CDPSession } from './cdp-session.js'; import type { ICDPSession } from './cdp-session.js';
export interface ReadResult { export interface ReadResult {
content: string; content: string;
totalLines: number; totalLines: number;
@@ -55,7 +55,7 @@ export declare class Editor {
private stylesheets; private stylesheets;
private sourceCache; private sourceCache;
constructor({ cdp }: { constructor({ cdp }: {
cdp: CDPSession; cdp: ICDPSession;
}); });
private setupEventListeners; private setupEventListeners;
/** /**
+3 -3
View File
@@ -5,7 +5,7 @@ The getStylesForLocator function inspects CSS styles applied to an element, simi
## Types ## Types
```ts ```ts
import type { CDPSession } from './cdp-session.js'; import type { ICDPSession } from './cdp-session.js';
import type { Locator } from 'playwright-core'; import type { Locator } from 'playwright-core';
export interface StyleSource { export interface StyleSource {
url: string; url: string;
@@ -25,9 +25,9 @@ export interface StylesResult {
inlineStyle: StyleDeclarations | null; inlineStyle: StyleDeclarations | null;
rules: StyleRule[]; rules: StyleRule[];
} }
export declare function getStylesForLocator({ locator, cdp, includeUserAgentStyles, }: { export declare function getStylesForLocator({ locator, cdp: cdpSession, includeUserAgentStyles, }: {
locator: Locator; locator: Locator;
cdp: CDPSession; cdp: ICDPSession;
includeUserAgentStyles?: boolean; includeUserAgentStyles?: boolean;
}): Promise<StylesResult>; }): Promise<StylesResult>;
export declare function formatStylesAsText(styles: StylesResult): string; export declare function formatStylesAsText(styles: StylesResult): string;