interceptor_chrome_navigate
Navigate Chrome tabs for proxy traffic capture by specifying target instances and URLs, ensuring accurate interception when monitoring network activity.
Instructions
Navigate a tab in a specific Chrome instance launched by interceptor_chrome_launch using that instance's CDP target WebSocket. Prevents cross-instance mistakes when proxy capture is required.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| target_id | Yes | Target ID from interceptor_chrome_launch | |
| url | Yes | Destination URL | |
| page_target_id | No | Optional page target ID from interceptor_chrome_cdp_info targets | |
| wait_for_proxy_capture | No | Wait for matching proxy traffic after navigate (default: true) | |
| timeout_ms | No | Max wait for CDP response and proxy capture (default: 5000ms) | |
| poll_interval_ms | No | Polling interval while waiting for proxy capture (default: 200ms) |
Implementation Reference
- src/tools/interceptors.ts:274-425 (handler)The handler function for "interceptor_chrome_navigate" which performs the navigation and optionally waits for proxy traffic.
async ({ target_id, url, page_target_id, wait_for_proxy_capture, timeout_ms, poll_interval_ms }) => { try { const chrome = interceptorManager.get("chrome"); if (!chrome) { return { content: [{ type: "text", text: JSON.stringify({ status: "error", error: "Chrome interceptor not registered." }) }] }; } const meta = await chrome.getMetadata(); const target = meta.activeTargets.find((t) => t.id === target_id); if (!target) { return { content: [{ type: "text", text: JSON.stringify({ status: "error", error: `Chrome target '${target_id}' not found. Is it still running?` }), }], }; } // Chrome interceptor stores CDP port in details.port // eslint-disable-next-line @typescript-eslint/no-explicit-any const details: any = target.details ?? {}; const port = details.port; if (typeof port !== "number" || !Number.isFinite(port) || port <= 0) { return { content: [{ type: "text", text: JSON.stringify({ status: "error", error: `Chrome target '${target_id}' has no valid CDP port.` }) }] }; } await waitForCdpVersion(port, { timeoutMs: timeout_ms, intervalMs: Math.max(50, Math.min(500, poll_interval_ms)), requestTimeoutMs: Math.min(1000, Math.max(100, poll_interval_ms)), }); const cdpTargets = await getCdpTargets(port, { timeoutMs: Math.min(timeout_ms, 2000) }); const pageTargets = cdpTargets.filter((t) => t.type === "page"); if (pageTargets.length === 0) { return { content: [{ type: "text", text: JSON.stringify({ status: "error", error: `No page targets available on Chrome target '${target_id}'.` }), }], }; } let selectedTarget: Record<string, unknown> | undefined; if (page_target_id) { selectedTarget = pageTargets.find((t) => t.id === page_target_id); if (!selectedTarget) { return { content: [{ type: "text", text: JSON.stringify({ status: "error", error: `Page target '${page_target_id}' not found on Chrome target '${target_id}'.`, }), }], }; } } else { selectedTarget = pageTargets.find((t) => { const tUrl = typeof t.url === "string" ? t.url.toLowerCase() : ""; return tUrl.length > 0 && !tUrl.startsWith("devtools://") && !tUrl.startsWith("chrome://"); }) ?? pageTargets[0]; } const pageTargetWs = selectedTarget.webSocketDebuggerUrl; if (typeof pageTargetWs !== "string" || pageTargetWs.length === 0) { return { content: [{ type: "text", text: JSON.stringify({ status: "error", error: `Selected page target has no webSocketDebuggerUrl on Chrome target '${target_id}'.`, }), }], }; } const beforeCount = proxyManager.getTraffic().length; const cdpResult = await sendCdpCommand( pageTargetWs, "Page.navigate", { url }, { timeoutMs: timeout_ms }, ); const destinationHost = normalizeHostname(url); let matchedExchangeIds: string[] = []; let sawAnyNewTraffic = false; let waitedMs = 0; if (wait_for_proxy_capture) { const startedAt = Date.now(); while (Date.now() - startedAt <= timeout_ms) { const delta = proxyManager.getTraffic().slice(beforeCount); if (delta.length > 0) { sawAnyNewTraffic = true; } if (destinationHost) { const matches = delta .filter((x) => { const host = x.request.hostname.toLowerCase(); return host === destinationHost || host.endsWith(`.${destinationHost}`); }) .map((x) => x.id); if (matches.length > 0) { matchedExchangeIds = matches; break; } } else if (delta.length > 0) { matchedExchangeIds = delta.map((x) => x.id); break; } await sleep(Math.max(50, poll_interval_ms)); waitedMs = Date.now() - startedAt; } } const delta = proxyManager.getTraffic().slice(beforeCount); const response: Record<string, unknown> = { status: "success", target_id, url, selected_page_target_id: selectedTarget.id ?? null, selected_page_url: selectedTarget.url ?? null, cdpResult, traffic: { beforeCount, afterCount: beforeCount + delta.length, deltaCount: delta.length, destinationHost, matchedHostExchangeCount: matchedExchangeIds.length, matchedHostExchangeIds: matchedExchangeIds, waitedMs, }, }; if (wait_for_proxy_capture && destinationHost && matchedExchangeIds.length === 0) { response.warning = sawAnyNewTraffic ? `Navigation succeeded but no '${destinationHost}' traffic was captured within ${timeout_ms}ms.` : `No new proxy traffic observed within ${timeout_ms}ms after navigation.`; } return { content: [{ type: "text", text: truncateResult(response), }], }; } catch (e) { return { content: [{ type: "text", text: JSON.stringify({ status: "error", error: errorToString(e) }) }] }; } }, - src/tools/interceptors.ts:262-273 (registration)The MCP tool registration for "interceptor_chrome_navigate".
server.tool( "interceptor_chrome_navigate", "Navigate a tab in a specific Chrome instance launched by interceptor_chrome_launch using that instance's CDP target WebSocket. Prevents cross-instance mistakes when proxy capture is required.", { target_id: z.string().describe("Target ID from interceptor_chrome_launch"), url: z.string().describe("Destination URL"), page_target_id: z.string().optional().describe("Optional page target ID from interceptor_chrome_cdp_info targets"), wait_for_proxy_capture: z.boolean().optional().default(true) .describe("Wait for matching proxy traffic after navigate (default: true)"), timeout_ms: z.number().optional().default(5000).describe("Max wait for CDP response and proxy capture (default: 5000ms)"), poll_interval_ms: z.number().optional().default(200).describe("Polling interval while waiting for proxy capture (default: 200ms)"), },