puppeteer_connect_active_tab
Connect to an existing Chrome instance with remote debugging enabled to automate browser tasks on active tabs.
Instructions
Connect to an existing Chrome instance with remote debugging enabled
Input Schema
TableJSON 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)Main execution logic for the puppeteer_connect_active_tab tool: fetches WebSocket endpoint, connects to existing browser, selects page, and 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:4-22 (schema)Input schema and metadata definition for the puppeteer_connect_active_tab tool.{ 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)MCP server registration for tool requests: ListTools returns the TOOLS array (including this tool's schema), CallTool dispatches to handleToolCall based on name.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS, })); server.setRequestHandler(CallToolRequestSchema, async (request) => handleToolCall(request.params.name, request.params.arguments ?? {}, state, server) );
- src/browser/connection.ts:28-42 (helper)Helper function to retrieve the WebSocket debugger URL from Chrome's remote debugging port endpoint.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}`); } }
- src/browser/connection.ts:44-111 (helper)Key helper function that connects Puppeteer to the existing browser using the WebSocket endpoint, discovers active tabs, selects the target page, and sets up logging.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; } }