{"_id":"@4bd4ll4h/mcp-devtools-browser","_rev":"3-70e9adfe0ce08d714679b1c21fdd7cc2","name":"@4bd4ll4h/mcp-devtools-browser","dist-tags":{"latest":"0.0.6"},"versions":{"0.0.4":{"name":"@4bd4ll4h/mcp-devtools-browser","version":"0.0.4","author":{"name":"abdallah abdelmahmoud"},"license":"MIT","_id":"@4bd4ll4h/mcp-devtools-browser@0.0.4","maintainers":[{"name":"4bd4ll4h","email":"4bd4ll4h.m@gmail.com"}],"homepage":"https://github.com/4bd4ll4h/mcp-devtools-browser#readme","bugs":{"url":"https://github.com/4bd4ll4h/mcp-devtools-browser/issues"},"bin":{"mcp-devtools-browser":"dist/index.js"},"dist":{"shasum":"fd361f9f05b61bfd122d737c74315cabc25f0984","tarball":"https://registry.npmjs.org/@4bd4ll4h/mcp-devtools-browser/-/mcp-devtools-browser-0.0.4.tgz","fileCount":45,"integrity":"sha512-kRnItochAI0tLTMJ71QDI+coqr/BS3Y6uySS04rhWhQ86kvUdgmN0Yhc8lGpzfVArjhtHfPUryQGI/ldsNy5pg==","signatures":[{"sig":"MEQCIDQvmjPBtMumnv5ebA76ddqk88I9rHNGGycr9sY4pE9nAiAaIh5KVeiDuCA7vK0OTk9y3OevOpZBvJVl7Vg66/iWaA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":199566},"type":"module","gitHead":"ffd41b834e36b43614cc72c099999ddbf4660226","scripts":{"build":"tsc && shx cp -r src/config dist/src/ && shx chmod +x dist/*.js","watch":"tsc --watch","prepare":"npm run build","inspector":"pnpx @modelcontextprotocol/inspector node ./dist/index.js"},"_npmUser":{"name":"4bd4ll4h","email":"4bd4ll4h.m@gmail.com"},"repository":{"url":"git+https://github.com/4bd4ll4h/mcp-devtools-browser.git","type":"git"},"_npmVersion":"10.9.4","description":"Exprimental MCP server using Puppeteer to control a browser instance foucsed on developer needs.","directories":{},"_nodeVersion":"22.21.0","dependencies":{"zod":"^3.24.2","dotenv":"^16.4.7","puppeteer":"24.26.1","async-mutex":"^0.4.1","mcps-logger":"^1.0.0","@toon-format/toon":"^2.0.0","@modelcontextprotocol/sdk":"^1.20.2"},"_hasShrinkwrap":false,"devDependencies":{"shx":"^0.3.4","typescript":"^5.6.2","@types/node":"^20.11.17"},"_npmOperationalInternal":{"tmp":"tmp/mcp-devtools-browser_0.0.4_1764096493273_0.9006830900006462","host":"s3://npm-registry-packages-npm-production"}},"0.0.5":{"name":"@4bd4ll4h/mcp-devtools-browser","version":"0.0.5","author":{"name":"abdallah abdelmahmoud"},"license":"MIT","_id":"@4bd4ll4h/mcp-devtools-browser@0.0.5","maintainers":[{"name":"4bd4ll4h","email":"4bd4ll4h.m@gmail.com"}],"homepage":"https://github.com/4bd4ll4h/mcp-devtools-browser#readme","bugs":{"url":"https://github.com/4bd4ll4h/mcp-devtools-browser/issues"},"bin":{"mcp-devtools-browser":"dist/index.js"},"dist":{"shasum":"885ded0ded1133f62b060e893493569091c7cedf","tarball":"https://registry.npmjs.org/@4bd4ll4h/mcp-devtools-browser/-/mcp-devtools-browser-0.0.5.tgz","fileCount":46,"integrity":"sha512-uAnxCZn+V+oV0WjccuXNhRReIL6f9ol3munPpmMJ7uzaqpz5gUx6gVOMdrY5WBvRVbiLR3o9PQ1t/s4pRJDZqw==","signatures":[{"sig":"MEQCIBzdf8lIfTp31UMJOi1Ph6m/uojm/HGfZUjHvr4iruAjAiBJgI/B58JpdETNluxCE2324rBg2xPxHgQfXwbKMw1Rlg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":202382},"type":"module","gitHead":"f45e21f282f6b0fccfaa2674caf953d3ad2a4940","scripts":{"build":"tsc && shx cp -r src/config dist/src/ && shx chmod +x dist/*.js","watch":"tsc --watch","prepare":"npm run build","inspector":"pnpx @modelcontextprotocol/inspector node ./dist/index.js"},"_npmUser":{"name":"4bd4ll4h","email":"4bd4ll4h.m@gmail.com"},"repository":{"url":"git+https://github.com/4bd4ll4h/mcp-devtools-browser.git","type":"git"},"_npmVersion":"10.9.4","description":"Exprimental MCP server using Puppeteer to control a browser instance foucsed on developer needs.","directories":{},"_nodeVersion":"22.21.0","dependencies":{"zod":"^3.24.2","dotenv":"^16.4.7","puppeteer":"24.26.1","async-mutex":"^0.4.1","mcps-logger":"^1.0.0","@toon-format/toon":"^2.0.0","@modelcontextprotocol/sdk":"^1.20.2"},"_hasShrinkwrap":false,"devDependencies":{"shx":"^0.3.4","typescript":"^5.6.2","@types/node":"^20.11.17"},"_npmOperationalInternal":{"tmp":"tmp/mcp-devtools-browser_0.0.5_1764098694086_0.5364394119093419","host":"s3://npm-registry-packages-npm-production"}},"0.0.6":{"name":"@4bd4ll4h/mcp-devtools-browser","version":"0.0.6","description":"Exprimental MCP server using Puppeteer to control a browser instance foucsed on developer needs.","license":"MIT","author":{"name":"abdallah abdelmahmoud"},"homepage":"https://github.com/4bd4ll4h/mcp-devtools-browser#readme","bugs":{"url":"https://github.com/4bd4ll4h/mcp-devtools-browser/issues"},"repository":{"type":"git","url":"git+https://github.com/4bd4ll4h/mcp-devtools-browser.git"},"type":"module","bin":{"mcp-devtools-browser":"dist/index.js"},"scripts":{"build":"tsc && shx cp -r src/config dist/src/ && shx chmod +x dist/*.js","prepare":"npm run build","inspector":"pnpx @modelcontextprotocol/inspector node ./dist/index.js","watch":"tsc --watch"},"dependencies":{"@modelcontextprotocol/sdk":"^1.20.2","@toon-format/toon":"^2.0.0","async-mutex":"^0.4.1","dotenv":"^16.4.7","mcps-logger":"^1.0.0","puppeteer":"24.26.1","zod":"^3.24.2"},"devDependencies":{"@types/node":"^20.11.17","shx":"^0.3.4","typescript":"^5.6.2"},"_id":"@4bd4ll4h/mcp-devtools-browser@0.0.6","gitHead":"35de8d51c47f96beec4433a3ce0f77859e450ecc","_nodeVersion":"22.21.0","_npmVersion":"10.9.4","dist":{"integrity":"sha512-XS5FBbdVZTi+I/NKj1S1LHmEwPv+IRe8FqxEOoGquVK3CHsDOTrgMw7jvO3WiBwtuSOfjozW3PUhBzO+glz0Cg==","shasum":"ed6e5cad9ca1270cd49dbc7d6573e25fe12ae2c8","tarball":"https://registry.npmjs.org/@4bd4ll4h/mcp-devtools-browser/-/mcp-devtools-browser-0.0.6.tgz","fileCount":50,"unpackedSize":214811,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDATnoJ+hIWQwv5yB5EmcfJJ268Ff00owcNBn4h3uzuaAIgRawT9qxsMqp17hvo23uv2p1upY5gVeBP7lCRAHjF9F4="}]},"_npmUser":{"name":"4bd4ll4h","email":"4bd4ll4h.m@gmail.com"},"directories":{},"maintainers":[{"name":"4bd4ll4h","email":"4bd4ll4h.m@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mcp-devtools-browser_0.0.6_1765155461803_0.3927701318554855"},"_hasShrinkwrap":false}},"time":{"created":"2025-11-25T18:48:13.209Z","modified":"2025-12-08T00:57:42.190Z","0.0.4":"2025-11-25T18:48:13.445Z","0.0.5":"2025-11-25T19:24:54.297Z","0.0.6":"2025-12-08T00:57:41.993Z"},"bugs":{"url":"https://github.com/4bd4ll4h/mcp-devtools-browser/issues"},"author":{"name":"abdallah abdelmahmoud"},"license":"MIT","homepage":"https://github.com/4bd4ll4h/mcp-devtools-browser#readme","repository":{"type":"git","url":"git+https://github.com/4bd4ll4h/mcp-devtools-browser.git"},"description":"Exprimental MCP server using Puppeteer to control a browser instance foucsed on developer needs.","maintainers":[{"name":"4bd4ll4h","email":"4bd4ll4h.m@gmail.com"}],"readme":"\r\n# DevTool Broswer for developers\r\n\r\n[![npm version](https://img.shields.io/npm/v/@4bd4ll4h/mcp-devtools-browser.svg)](https://www.npmjs.com/package/@4bd4ll4h/mcp-devtools-browser)\r\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\r\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.6+-blue.svg)](https://www.typescriptlang.org/)\r\n\r\n> A powerful Model Context Protocol (MCP) server that provides LLMs with browser automation capabilities using Puppeteer.\r\n\r\n## 🚀 Quick Start\r\n\r\n```bash\r\nnpm install -g @4bd4ll4h/mcp-devtools-browser\r\n```\r\n\r\nAdd to your MCP client configuration:\r\n\r\n```json\r\n{\r\n  \"mcpServers\": {\r\n    \"devtools-browser\": {\r\n      \"command\": \"npx\",\r\n      \"args\": [\"@4bd4ll4h/mcp-devtools-browser\"]\r\n    }\r\n  }\r\n}\r\n```\r\n\r\n## ✨ Features\r\n\r\n- **Browser Automation**: Open, navigate, and control browser pages\r\n- **DOM Inspection**: Extract structured DOM data with accessibility focus\r\n- **Network Monitoring**: Capture and analyze network requests\r\n- **Event Logging**: Comprehensive session tracking and debugging\r\n- **User Interactions**: Click, type, scroll, hover, and more\r\n- **Visual Capture**: Screenshots and visual analysis\r\n- **Resource Management**: Automatic cleanup and memory management\r\n\r\n## 📖 Documentation\r\n\r\n- [API Reference](API.md) - Complete tool and resource documentation\r\n- [Examples](EXAMPLES.md) - Usage examples and tutorials\r\n- [Development Guide](DEVELOPMENT.md) - Architecture and contribution guidelines\r\n- [Contributing](CONTRIBUTING.md) - How to contribute to this project\r\n\r\n## 🤝 Contributing\r\n\r\nWe welcome contributions! Please read our [Contributing Guide](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md).\r\n\r\n---\r\n\r\n## Project Specification\r\n\r\n### **Project Overview**\r\n\r\nThis project aims to build a **Model Context Protocol (MCP)** server that assists an LLM in generating **high-quality, reliable web-scraping scripts** using **TypeScript + Puppeteer**.\r\n\r\nThe MCP will act as a managed gateway between:\r\n\r\n* A real browser environment (Puppeteer)\r\n* Page lifecycle events (requests, responses, selectors, DOM state)\r\n* An LLM tasked with understanding the page and generating scraping actions\r\n\r\nThe goal:\r\n\r\n> Allow the LLM to autonomously explore pages, inspect network traffic, extract DOM node paths/selectors, and generate robust scraping scripts on demand.\r\n\r\n---\r\n\r\n## **Core Use-Cases**\r\n\r\n1. **Data Extraction From Any Website**\r\n\r\n   * Offers, products, tables, PDFs, metadata, images\r\n\r\n2. **Network Intelligence**\r\n\r\n   * Detect backend API calls\r\n   * Infer JSON data structures\r\n   * Prioritize structured data over rendered HTML\r\n\r\n3. **Dynamic DOM Inspection**\r\n\r\n   * Choose stable selectors\r\n   * Scroll and lazily load content\r\n   * Handle shadow DOM, iframes, modals\r\n\r\n---\r\n\r\n## **Why an MCP?**\r\n\r\nMCP provides:\r\n\r\n* Structured bidirectional workflow\r\n* Model actions with schema\r\n* Better orchestration\r\n* Reproducibility\r\n\r\nIt enables constructing **LLM-driven scraping agents**.\r\n\r\n---\r\n\r\n## **Primary Technical Challenges**\r\n\r\nThis system must solve:\r\n\r\n### ✅ Exposing DevTools-like insights to an LLM\r\n\r\n* Network requests/responses\r\n* Headers, bodies, error codes\r\n\r\n### ✅ Large DOM visibility\r\n\r\n* Without overloading token limits\r\n\r\n### ✅ Robust stateful browsing\r\n\r\n* Tabs\r\n* Navigation history\r\n* Parallel extraction\r\n\r\n---\r\n\r\n## **High-Level Architecture**\r\n\r\n```\r\nLLM <--> MCP Server <--> Puppeteer Controller\r\n                     |\r\n                     ├── Browser Pool (multi-page sessions)\r\n                     ├── Network Listener\r\n                     └── DOM Snapshot Manager\r\n```\r\n\r\n---\r\n\r\n## **Key Components**\r\n\r\n### 1. **Browser Manager**\r\n\r\nResponsibilities:\r\n\r\n* Start/stop browser instances\r\n* Create new tabs\r\n* Close tabs\r\n* Report session info\r\n\r\nRecommendations:\r\n\r\n* Maintain an internal registry keyed by `sessionId`\r\n* One session per LLM conversation\r\n\r\n### 2. **Tab/Page Manager**\r\n\r\nFor each page:\r\n\r\n* Navigation\r\n* Click, Type, Scroll, WaitForSelector\r\n* Save screenshots\r\n* Persist page state\r\n\r\nRecommended:\r\n\r\n* Limit max open tabs\r\n* Auto-cleanup resources\r\n\r\n### 3. **Network Interceptor**\r\n\r\nGoals:\r\n\r\n* Capture all XHR/fetch calls\r\n* Inspect responses\r\n* Identify API endpoints\r\n* Detect potential structured data sources\r\n\r\nMCP tool actions:\r\n\r\n* `getNetworkRequests()`\r\n* `filterRequestsBy(url|type|status)`\r\n* `fetchResponseBody(requestId)`\r\n\r\n### 4. **DOM Inspector**\r\n\r\nChallenge:\r\nPages can be huge; LLM token limits apply.\r\n\r\nApproaches:\r\n\r\n#### **A. DOM Chunking**\r\n\r\nSplit the DOM into slices:\r\n\r\n* By depth\r\n* By visual viewport\r\n* By selector path\r\n\r\n#### **B. Selector Spotlight**\r\n\r\nLLM requests:\r\n\r\n* Highlight possible selectors for hovered element or query\r\n\r\n#### **C. CSS Path Generation**\r\n\r\nAutomatically compute:\r\n\r\n* CSS selectors\r\n* XPath\r\n* Robust heuristic selectors\r\n\r\n---\r\n\r\n## **Recommended LLM-Facing MCP Actions**\r\n\r\n### Navigation\r\n\r\n* `navigate(url)`\r\n* `goBack()`\r\n* `goForward()`\r\n\r\n### DOM Interrogation\r\n\r\n* `querySelector(selector)`\r\n* `querySelectorAll(selector, limit)`\r\n* `extractAttributes(selector, attrs[])`\r\n* `getBoundingClientRect(selector)`\r\n* `scroll(amount)`\r\n* `scrollToBottom()`\r\n\r\n### Network\r\n\r\n* `listNetworkRequests(type?)`\r\n* `getRequestDetails(requestId)`\r\n* `getResponseBody(requestId)`\r\n\r\n### Screenshots\r\n\r\n* `captureScreenshot(mode=viewport|full)`\r\n\r\n### Debugging\r\n\r\n* `printConsoleLogs()`\r\n* `printNetworkErrors()`\r\n\r\n### Utility\r\n\r\n* `generateSelectorsAtPoint(x,y)`\r\n\r\n---\r\n\r\n## **Browser/Tab Lifecycle Strategy**\r\n\r\n### Session Rules\r\n\r\n* Each MCP session creates **one browser instance**\r\n* Tabs are registered and tracked\r\n* Hard limit (e.g., 5) to avoid memory blowup\r\n\r\n### Garbage Collection\r\n\r\n* Idle tabs > N minutes → auto close\r\n* Close all tabs on session end\r\n\r\n### Tab Identification\r\n\r\nReturn structured tab state:\r\n\r\n```json\r\n{\r\n  \"tabId\": \"abc123\",\r\n  \"url\": \"...\",\r\n  \"title\": \"...\",\r\n  \"loading\": false\r\n}\r\n```\r\n\r\n---\r\n\r\n## **Exposing DevTools-Like Capabilities**\r\n\r\n### Approach A — Chrome DevTools Protocol Events\r\n\r\nUse:\r\n\r\n* `page.on('request')`\r\n* `page.on('response')`\r\n* `page.on('console')`\r\n\r\nPros:\r\n\r\n* Real-time\r\n* Low overhead\r\n\r\n### Approach B — Intercept & Store Network History\r\n\r\nStore:\r\n\r\n* Method\r\n* URL\r\n* Status\r\n* Request body\r\n* Response size/body hints\r\n\r\nLet the LLM filter later.\r\n\r\n### Approach C — Filter by Type\r\n\r\n* XHR\r\n* Fetch\r\n* Media\r\n* Stylesheet\r\n* Script\r\n\r\nUseful for target discovery.\r\n\r\n**Recommended**: All three.\r\n\r\n---\r\n\r\n## **Making Large DOMs LLM-Friendly**\r\n\r\n### Approach A — Contextual Chunking\r\n\r\nSplit DOM by:\r\n\r\n* visible sections\r\n* semantic regions (`<section>`, `<article>`)\r\n\r\n### Approach B — Selector-Only Summaries\r\n\r\nInstead of dumping HTML, provide:\r\n\r\n```\r\nselector -> value summary\r\n```\r\n\r\nExample:\r\n\r\n```\r\n.OfferTitle -> \"Summer Sale\"\r\n.OfferPrice -> \"$19.99\"\r\n```\r\n\r\n### Approach C — On-Demand Snapshot\r\n\r\nLLM asks:\r\n\r\n> \"Give me the DOM for the 'products' container\"\r\n\r\nYou respond with localized HTML only.\r\n\r\n### Recommended\r\n\r\nA + C combination.\r\n\r\n---\r\n\r\n## **Your Suggested Way (Evaluation)**\r\n\r\n### Strengths\r\n\r\n✔ Monitoring requests catches hidden APIs\r\n✔ DOM extraction enables visual scraping\r\n✔ Intercepting lifecycle gives completeness\r\n\r\n### Weaknesses / Risks\r\n\r\n⚠ Dumping full DOM = token explosion\r\n⚠ JSON responses can be massive\r\n⚠ Too many network logs → noise\r\n⚠ Repeated structure confusion for LLMs\r\n\r\n### Potential Quality Issues\r\n\r\n* Selector instability (dynamic classes)\r\n* Infinite scroll complexity\r\n* Event timing issues\r\n* CSP blocking screenshots\r\n\r\nWe will mitigate these using heuristics and detection rules.\r\n\r\n---\r\n\r\n## **Recommended Selector Stability Heuristics**\r\n\r\n1. Prefer:\r\n\r\n* `data-*` attributes\r\n* Semantic HTML\r\n* Parent chains\r\n\r\n2. Avoid:\r\n\r\n* Obfuscated class names\r\n* Auto-generated IDs\r\n\r\n3. Validate:\r\n\r\n* That selector matches consistent count across scroll events\r\n\r\n---\r\n\r\n## **Script Generation Philosophy**\r\n\r\nHorizontal fallback order:\r\n\r\n1. Structured API JSON (Best)\r\n2. Semantic HTML\r\n3. Computed DOM text\r\n4. Visual scraping (worst)\r\n\r\nThe LLM should operate with this hierarchy.\r\n\r\n---\r\n\r\n## **MCP Tool Schema Examples**\r\n\r\n### Example Action: List Network Requests\r\n\r\n```ts\r\n{\r\n  \"name\": \"listNetworkRequests\",\r\n  \"arguments\": {\r\n    \"type\": \"xhr\",\r\n    \"status\": 200,\r\n    \"contains\": \"offers\"\r\n  }\r\n}\r\n```\r\n\r\n### Example Action: Query DOM\r\n\r\n```ts\r\n{\r\n  \"name\": \"querySelectorAll\",\r\n  \"arguments\": {\r\n    \"selector\": \".offer-card\",\r\n    \"limit\": 20,\r\n    \"attributes\": [\"href\", \"innerText\"]\r\n  }\r\n}\r\n```\r\n\r\n---\r\n\r\n## **LLM Workflow Example**\r\n\r\n1. Navigate to target URL\r\n2. Monitor network for JSON endpoints\r\n3. Request DOM snapshot of target regions\r\n4. Choose stable selectors\r\n5. Generate a reusable scraping script in TS\r\n6. Test selectors on multiple pages (if pagination)\r\n7. Output structured results\r\n\r\n---\r\n\r\n## **Error Handling Strategy**\r\n\r\n* Expose structured errors\r\n* Include stack traces\r\n* Inform LLM of transient failures\r\n\r\nExample:\r\n\r\n```json\r\n{\r\n  \"error\": \"SelectorNotFound\",\r\n  \"selector\": \".price\",\r\n  \"attempts\": 3\r\n}\r\n```\r\n\r\n---\r\n\r\n## **Future Extensions**\r\n\r\n* PDF downloading\r\n* File metadata extraction\r\n* Accessibility tree scraping\r\n* Snapshot diff detection\r\n* Session replay\r\n\r\n---\r\n\r\n## **Security Considerations**\r\n\r\n* Do not allow navigation to `localhost` ports\r\n* Disable downloads by default\r\n* Sanitize file output paths\r\n* Strip sensitive request headers\r\n\r\n---\r\n\r\n## **Technology Stack**\r\n\r\n**Language**: TypeScript\r\n**Browser Automation**: Puppeteer\r\n**Protocol**: MCP\r\n**State Storage**: In-memory map\r\n**Parser Tools**:\r\n\r\n* DOM traversal utilities\r\n* CSS/XPath generator libraries\r\n\r\n---\r\n\r\n## **Folder Structure (Proposed)**\r\n\r\n```\r\n/src\r\n  /mcp\r\n    actions/\r\n    schemas/\r\n    router.ts\r\n  /browser\r\n    BrowserManager.ts\r\n    PageManager.ts\r\n    NetworkTracker.ts\r\n    DomInspector.ts\r\n  utils/\r\n  index.ts\r\n  types.ts\r\n```\r\n\r\n---\r\n\r\n## **Success Criteria**\r\n\r\n✅ The LLM can:\r\n\r\n* Inspect network calls\r\n* Read DOM structure safely\r\n* Navigate tabs\r\n* Identify stable selectors\r\n* Generate robust scripts\r\n\r\n✅ The agent:\r\n\r\n* Avoids full DOM dumps\r\n* Uses API endpoints when possible\r\n* Extracts structured results reliably\r\n\r\n---\r\n\r\n## **End Goal**\r\n\r\nA fully autonomous scraping assistant that can:\r\n\r\n* Discover data sources\r\n* Generate resilient extraction logic\r\n* Produce TypeScript/Node scripts\r\n* Handle dynamic web apps\r\n\r\n---\r\n\r\n### **Ready to Build**\r\n\r\nNow Cursor AI has:\r\n\r\n* Global context\r\n* Architecture\r\n* Best-practice heuristics\r\n* Risks\r\n* Workflows\r\n* Expected APIs\r\n\r\nThis file should power smart context-aware coding assistance.\r\n\r\nLet me know when you're ready for:\r\n\r\n* Code scaffolding\r\n* MCP action definitions\r\n* Puppeteer wrapper implementations\r\n* Selector heuristics\r\n* JSON schema contracts\r\n","readmeFilename":"README.md"}