diff --git a/extensions/browser/src/browser/chrome-mcp.test.ts b/extensions/browser/src/browser/chrome-mcp.test.ts index 221b15cac14..33f2dcd993d 100644 --- a/extensions/browser/src/browser/chrome-mcp.test.ts +++ b/extensions/browser/src/browser/chrome-mcp.test.ts @@ -1,6 +1,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +import { MAX_TIMER_TIMEOUT_MS } from "openclaw/plugin-sdk/number-runtime"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { clickChromeMcpCoords, @@ -12,6 +13,7 @@ import { listChromeMcpTabs, navigateChromeMcpPage, openChromeMcpTab, + resolveChromeMcpNavigateCallTimeoutMs, resetChromeMcpSessionsForTest, setChromeMcpProcessCleanupDepsForTest, setChromeMcpSessionFactoryForTest, @@ -922,6 +924,11 @@ describe("chrome MCP page parsing", () => { expect(navigateCall?.arguments?.timeout).toBe(20_000); }); + it("caps the navigate_page safety-net timeout", () => { + expect(resolveChromeMcpNavigateCallTimeoutMs(10_000)).toBe(15_000); + expect(resolveChromeMcpNavigateCallTimeoutMs(Number.MAX_VALUE)).toBe(MAX_TIMER_TIMEOUT_MS); + }); + it("resets the Chrome MCP session when a navigate_page call hangs past the safety-net timeout", async () => { vi.useFakeTimers(); let factoryCalls = 0; diff --git a/extensions/browser/src/browser/chrome-mcp.ts b/extensions/browser/src/browser/chrome-mcp.ts index c4ef348e907..09a63054d5c 100644 --- a/extensions/browser/src/browser/chrome-mcp.ts +++ b/extensions/browser/src/browser/chrome-mcp.ts @@ -8,6 +8,7 @@ import { promisify } from "node:util"; import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; import { + addTimerTimeoutGraceMs, parseStrictPositiveInteger, resolveNonNegativeIntegerOption, } from "openclaw/plugin-sdk/number-runtime"; @@ -1213,6 +1214,7 @@ export async function navigateChromeMcpPage(params: { timeoutMs?: number; }): Promise<{ url: string }> { const resolvedTimeoutMs = params.timeoutMs ?? CHROME_MCP_NAVIGATE_TIMEOUT_MS; + const callTimeoutMs = resolveChromeMcpNavigateCallTimeoutMs(resolvedTimeoutMs); await callTool( params.profileName, chromeMcpProfileOptionsFromParams(params), @@ -1223,7 +1225,7 @@ export async function navigateChromeMcpPage(params: { url: params.url, timeout: resolvedTimeoutMs, }, - { timeoutMs: resolvedTimeoutMs + 5_000 }, + { timeoutMs: callTimeoutMs }, ); const page = await findPageById( params.profileName, @@ -1233,6 +1235,10 @@ export async function navigateChromeMcpPage(params: { return { url: page.url ?? params.url }; } +export function resolveChromeMcpNavigateCallTimeoutMs(timeoutMs: number): number { + return addTimerTimeoutGraceMs(timeoutMs) ?? 1; +} + export async function takeChromeMcpSnapshot(params: { profileName: string; profile?: ChromeMcpProfileOptions;