puppeteer_connect_active_tab
Attach to an active Chrome tab via remote debugging port to automate browser actions with Puppeteer.
Instructions
Connect to an existing Chrome instance with remote debugging enabled
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| targetUrl | No | Optional URL of the target tab to connect to. If not provided, connects to the first available tab. | |
| debugPort | No | Optional Chrome debugging port (default: 9222) |
Implementation Reference
- src/tools/handlers.ts:23-64 (handler)Handler case in handleToolCall switch: gets WebSocket URL from Chrome debug port, connects to existing browser, returns success/error response.
case "puppeteer_connect_active_tab": try { const wsEndpoint = await getDebuggerWebSocketUrl(args.debugPort); const connectedPage = await connectToExistingBrowser( wsEndpoint, args.targetUrl, (logEntry) => { state.consoleLogs.push(logEntry); notifyConsoleUpdate(server); } ); const url = await connectedPage.url(); const title = await connectedPage.title(); return { content: [{ type: "text", text: `Successfully connected to browser\nActive webpage: ${title} (${url})`, }], isError: false, }; } catch (error) { const errorMessage = (error as Error).message; const isConnectionError = errorMessage.includes('connect to Chrome debugging port') || errorMessage.includes('Target closed'); return { content: [{ type: "text", text: `Failed to connect to browser: ${errorMessage}\n\n` + (isConnectionError ? "To connect to Chrome:\n" + "1. Close Chrome completely\n" + "2. Reopen Chrome with remote debugging enabled:\n" + " Windows: chrome.exe --remote-debugging-port=9222\n" + " Mac: /Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome --remote-debugging-port=9222\n" + "3. Navigate to your desired webpage\n" + "4. Try the operation again" : "Please check if Chrome is running and try again.") }], isError: true, }; } - src/tools/definitions.ts:5-22 (schema)Tool definition with inputSchema: optional targetUrl (string) and debugPort (number, default 9222).
name: "puppeteer_connect_active_tab", description: "Connect to an existing Chrome instance with remote debugging enabled", inputSchema: { type: "object", properties: { targetUrl: { type: "string", description: "Optional URL of the target tab to connect to. If not provided, connects to the first available tab." }, debugPort: { type: "number", description: "Optional Chrome debugging port (default: 9222)", default: 9222 } }, required: [], }, }, - src/server.ts:35-41 (registration)Registration: TOOLS array (including puppeteer_connect_active_tab) is registered via ListToolsRequestSchema, and tool calls are dispatched via CallToolRequestSchema to handleToolCall.
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS, })); server.setRequestHandler(CallToolRequestSchema, async (request) => handleToolCall(request.params.name, request.params.arguments ?? {}, state, server) ); - src/browser/connection.ts:44-111 (helper)Helper function: connects to an existing Chrome browser via WebSocket, finds active non-extension pages, selects a target by URL (or first available), configures headers and console listeners.
export async function connectToExistingBrowser( wsEndpoint: string, targetUrl?: string, onConsoleMessage?: (logEntry: string) => void ): Promise<Page> { logger.info('Connecting to existing browser', { wsEndpoint, targetUrl }); try { // If we have an existing Puppeteer-launched browser, close it if (browser && !browser.isConnected()) { logger.debug('Closing existing browser connection'); await browser.close(); browser = undefined; page = undefined; } // Connect to the browser instance with null viewport to maintain browser's viewport logger.debug('Establishing connection to browser'); browser = await puppeteer.connect({ browserWSEndpoint: wsEndpoint, defaultViewport: null }); logger.info('Successfully connected to browser'); // Get all pages and find non-extension pages const pages = await browser.pages(); const activeTabs: ActiveTab[] = []; for (const p of pages) { const url = await p.url(); if (!url.startsWith('chrome-extension://')) { const title = await p.title(); logger.info('Found active webpage:', { url, title }); activeTabs.push({ page: p, url, title }); } } if (activeTabs.length === 0) { throw new Error("No active non-extension pages found in the browser"); } // Select appropriate page if (targetUrl) { // Find the page with matching URL const targetTab = activeTabs.find(tab => tab.url === targetUrl); page = targetTab ? targetTab.page : activeTabs[0].page; } else { // Use the first active non-extension page page = activeTabs[0].page; } // Configure page settings await page.setExtraHTTPHeaders({ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' }); // Set up console message handling if (onConsoleMessage) { page.on("console", (msg) => { const logEntry = `[${msg.type()}] ${msg.text()}`; onConsoleMessage(logEntry); }); } return page; } catch (error) { throw error; } } - src/browser/connection.ts:28-42 (helper)Helper function: fetches the WebSocket debugger URL from the Chrome DevTools Protocol endpoint at localhost:{port}/json/version.
export async function getDebuggerWebSocketUrl(port: number = 9222): Promise<string> { try { const response = await fetch(`http://localhost:${port}/json/version`); if (!response.ok) { throw new Error(`Failed to fetch debugger info: ${response.statusText}`); } const data = await response.json(); if (!data.webSocketDebuggerUrl) { throw new Error("No WebSocket debugger URL found. Is Chrome running with --remote-debugging-port?"); } return data.webSocketDebuggerUrl; } catch (error) { throw new Error(`Failed to connect to Chrome debugging port ${port}: ${(error as Error).message}`); } }