{"owner":"ChromeDevTools","repo":"chrome-devtools-mcp","hasSkills":true,"totalSkillsCount":21,"totalTokensCount":18568,"categories":["marketplace","plugin-manifest","root-instruction","anthropic-skill"],"hasMcp":false,"mcpConfig":null,"found":[".claude-plugin/marketplace.json",".claude-plugin/plugin.json",".cursor-plugin/plugin.json",".github/plugin/plugin.json",".kimi-code-plugin/plugin.json","AGENTS.md","plugin.json","skills/a11y-debugging/SKILL.md","skills/a11y-debugging/references/a11y-snippets.md","skills/chrome-devtools-cli/SKILL.md","skills/chrome-devtools-cli/references/installation.md","skills/chrome-devtools/SKILL.md","skills/cookie-debugging/SKILL.md","skills/debug-optimize-lcp/SKILL.md","skills/debug-optimize-lcp/references/elements-and-size.md","skills/debug-optimize-lcp/references/lcp-breakdown.md","skills/debug-optimize-lcp/references/lcp-snippets.md","skills/debug-optimize-lcp/references/optimization-strategies.md","skills/memory-leak-debugging/SKILL.md","skills/memory-leak-debugging/references/common-leaks.md","skills/troubleshooting/SKILL.md"],"skills":{".claude-plugin/marketplace.json":"{\n  \"name\": \"chrome-devtools-plugins\",\n  \"version\": \"1.12.0\",\n  \"description\": \"Bundled plugins for actuating and debugging the Chrome browser.\",\n  \"repository\": \"https://github.com/ChromeDevTools/chrome-devtools-mcp\",\n  \"owner\": {\n    \"name\": \"Chrome DevTools Team\",\n    \"email\": \"devtools-dev@chromium.org\"\n  },\n  \"plugins\": [\n    {\n      \"name\": \"chrome-devtools-mcp\",\n      \"source\": \"./\",\n      \"description\": \"Reliable automation, in-depth debugging, and performance analysis in Chrome using Chrome DevTools and Puppeteer\"\n    }\n  ]\n}\n",".claude-plugin/plugin.json":"{\n  \"name\": \"chrome-devtools-mcp\",\n  \"version\": \"1.12.0\",\n  \"description\": \"Reliable automation, in-depth debugging, and performance analysis in Chrome using Chrome DevTools and Puppeteer\",\n  \"mcpServers\": {\n    \"chrome-devtools\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"chrome-devtools-mcp@1.12.0\"\n      ]\n    }\n  }\n}\n",".cursor-plugin/plugin.json":"{\n  \"name\": \"devtools-for-agents\",\n  \"description\": \"Help your agent build, debug, and verify your code correctly. With Chrome DevTools for agents, your AI agent can interact with the Chrome browser to test code, emulate users, and catch bugs using Chrome DevTools’ capabilities before shipping.\",\n  \"version\": \"1.12.0\",\n  \"author\": {\n    \"name\": \"Google Chrome\"\n  },\n  \"logo\": \"https://github.com/ChromeDevTools/devtools-logo/blob/master/logos/svg/chrome-devtools-square-responsive.svg\",\n  \"homepage\": \"https://developer.chrome.com/docs/devtools/agents\",\n  \"repository\": \"https://github.com/ChromeDevTools/chrome-devtools-mcp\",\n  \"mcpServers\": {\n    \"chrome-devtools\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"chrome-devtools-mcp@1.12.0\"\n      ]\n    }\n  }\n}\n",".github/plugin/plugin.json":"{\n  \"name\": \"chrome-devtools-mcp\",\n  \"version\": \"1.12.0\",\n  \"description\": \"Reliable automation, in-depth debugging, and performance analysis in Chrome using Chrome DevTools and Puppeteer\",\n  \"mcpServers\": {\n    \"chrome-devtools\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"chrome-devtools-mcp@1.12.0\"\n      ]\n    }\n  }\n}\n",".kimi-code-plugin/plugin.json":"{\n  \"name\": \"chrome-devtools\",\n  \"description\": \"Help your agent build, debug, and verify your code correctly. With Chrome DevTools for agents, your AI agent can interact with the Chrome browser to test code, emulate users, and catch bugs using Chrome DevTools’ capabilities before shipping.\",\n  \"version\": \"1.12.0\",\n  \"author\": {\n    \"name\": \"Google Chrome\"\n  },\n  \"logo\": \"https://github.com/ChromeDevTools/devtools-logo/blob/master/logos/svg/chrome-devtools-square-responsive.svg\",\n  \"homepage\": \"https://developer.chrome.com/docs/devtools/agents\",\n  \"repository\": \"https://github.com/ChromeDevTools/chrome-devtools-mcp\",\n  \"mcpServers\": {\n    \"chrome-devtools\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"chrome-devtools-mcp@1.12.0\"\n      ]\n    }\n  }\n}\n","AGENTS.md":"This repository contains an MCP server and CLI for Chrome DevTools.\n\n# Instructions\n\n- Use only scripts from `package.json` to run commands.\n- Use `npm run build` to run tsc and test build.\n- Use `npm run test` to build and run tests, run all tests to verify correctness.\n- Use `npm run test path-to-test.ts` to build and run a single test file, for example, `npm run test tests/McpContext.test.ts`.\n- Use `npm run format` to fix formatting and get linting errors.\n- Never modify `third_party/devtools-frontend` except for experimentation: it is a git submodule, a mirror of the actual codebase.\n\n## Rules for TypeScript\n\n- Do not use `any` type.\n- Do not use `as` keyword for type casting.\n- Do not use `!` operator for type assertion.\n- Do not use `// @ts-ignore` comments.\n- Do not use `// @ts-nocheck` comments.\n- Do not use `// @ts-expect-error` comments.\n- Prefer `for..of` instead of `forEach`.\n- Never type-check types that are already type safe (e.g. redundant `typeof` checks on statically typed variables).\n\n## Rules for Testing\n\n### Structure and Separation of Concerns\n\n- **Prefer mock-based unit tests over real-browser tests**: Do not use `withMcpContext` or launch a real browser unless the test genuinely requires real browser or DevTools protocol integration (e.g., live CDP events, browser lifecycle, secondary sessions). Puppeteer already tests browser behavior upstream; unit tests run faster and avoid browser overhead.\n- **Tool handler tests (`tests/tools/*.test.ts`)**:\n  - Test that the tool handler parses/validates parameters and invokes the corresponding methods on `page`, `context`, or `response` with the exact expected arguments.\n  - Do **not** reimplement business logic or state tracking inside mocks (e.g., do not simulate state changes in mock methods).\n- **Core class tests (e.g., `tests/McpPage.test.ts`)**:\n  - Test business logic by instantiating the real class under test with mocked dependencies (e.g., instantiate `new McpPage(...)` with a mocked Puppeteer page from `createMockPuppeteerPage()`).\n  - Assert that the class calls the underlying Puppeteer methods with the expected parameters.\n\n### Mocking Guidelines (`tests/mocks.ts`)\n\n- **Centralize mocks in `tests/mocks.ts`**: Keep all reusable mock factories in `tests/mocks.ts`. Import directly from `tests/mocks.ts` (do not re-export from `tests/utils.ts`).\n- **Use `sinon.createStubInstance(Class)`**: Do not hand-roll mock objects or define custom mock interfaces. Use `sinon.createStubInstance()` so all prototype methods are automatically stubbed.\n- **Typing**: Use `sinon.SinonStubbedInstance<Class>` for mock types (e.g., `MockMcpPage`, `MockMcpContext`, `MockMcpResponse`).\n- **Handler mocks helper**: For tool handlers, use `const {page, context, response} = createHandlerMocks();` from `tests/mocks.ts` to set up all three mocks in one call.\n- **Keep mocks generic**: Do not tailor mocks to a specific tool or test suite.\n- **Naming conventions**: Use `mock` rather than `fake` in helper and variable names (e.g., `createMockPuppeteerPage`, `createMockMcpPage`). Name the mocked Puppeteer page instance `pptrPage`.\n\n### Assertions and Sinon Best Practices\n\n- **Use `sinon.assert` methods**: Do not use Node's `assert.ok(stub.calledOnce)` or `assert.deepStrictEqual(stub.firstCall.args[0], ...)` to verify stub calls.\n- **Verify exact arguments**:\n  - Use `sinon.assert.calledOnceWithExactly(stub, ...args)` for single calls with exact arguments.\n  - Use `sinon.assert.calledWithExactly(stub.secondCall, ...args)` for subsequent calls.\n  - Use `sinon.assert.notCalled(stub)` to assert a method was not invoked.\n- **Clean up stubs**: `tests/setup.ts` runs `sinon.restore()` after every test. Do not add `afterEach(() => sinon.restore());` to test suites.\n\n### Test Cleanliness\n\n- Do not add redundant comments or verbose JSDoc for short, self-describing mock functions or tests.\n- Only test real scenarios; avoid testing redundant or artificial calls that cannot happen in real usage.\n- Use current year (2026) in copyright headers for new test files.\n\n# Pre-submit checklist\n\nComplete every item before you upload a PR or push a new revision.\n\n## Scope and PR description\n\n- [ ] The PR contains one change. Move unrelated refactors, renames,\n      formatting changes, and build or compiler changes into their own PR.\n- [ ] The PR title, the commit type (`fix:`, `feat:`, `chore:`), and the\n      squash commit message describe the same change. A change to a default\n      value or to tool output is a `fix:` or a `feat:`, not a `chore:`.\n- [ ] The PR description lists every user-visible behavior change, including\n      changes to other tools that share the changed code path.\n- [ ] The PR description matches the shipped behavior. Remove claims about\n      fixes that the diff does not contain.\n- [ ] The PR description names new functions that have no production caller\n      yet, or the PR removes them.\n- [ ] The branch has no merge conflicts with `main`. Rebase only when a\n      conflict exists.\n- [ ] `npm run gen` ran after a change to a tool schema, a tool description,\n      or a CLI option. The generated files are committed.\n- [ ] `npm run format` ran. No line is longer than 80 columns.\n- [ ] `npm run test` passed locally. CI failures on all platforms are your\n      responsibility.\n\n## Design\n\n- [ ] The change lives in the lowest layer that owns the data: page logic in\n      `McpPage`, context logic in `McpContext`, formatting in\n      `src/formatters/*`. `McpResponse` does not resolve pages or workers.\n- [ ] If Puppeteer or devtools-frontend owns the bug, fix it there first. Do\n      not copy or re-implement upstream code in this repository.\n- [ ] Puppeteer internals are imported only through `src/third_party/index.ts`.\n- [ ] Repeated code is one helper. Extract a helper when the same block\n      appears two or more times. Prefer an existing helper or a Puppeteer\n      utility (for example `Deferred`) over a new one.\n- [ ] A new formatter class exposes `toJSON()` and `toString()`, and\n      `toString()` uses the `toJSON()` output.\n- [ ] A function with three or more optional parameters takes one options\n      object. Do not pass positional `undefined` placeholders.\n- [ ] Each default value is declared in one place.\n- [ ] A new behavior is opt-in by default when a wrong classification fails\n      silently (for example a config option is restart-required unless the\n      author marks it reloadable).\n- [ ] An invalid combination of flags or options is rejected by the type\n      system where possible, not only by a comment.\n- [ ] No public method exists only to serve a test. Put shared helpers in\n      `src/utils/*`.\n- [ ] Parameter validation lives in the Zod schema (`refine`, `coerce`), not\n      in the handler. Validation runs before the handler changes any state.\n- [ ] Numeric validation matches what the consumer accepts. Use\n      `Number.isInteger` and a range check where CDP expects integers.\n\n## Correctness and edge cases\n\n- [ ] A failed external call does not leave stale state. Restore the previous\n      value when a call rejects.\n- [ ] An error path still produces the artifacts and partial results the\n      agent needs before it throws.\n- [ ] An async operation that a timeout can abandon checks that its target\n      (browser, page, config) is still current when it resumes.\n- [ ] A lock or guard is acquired and released exactly once. A helper that\n      releases a guard throws when the guard is already released.\n- [ ] Concurrent callbacks (watchers, reloads, event handlers) are\n      serialized. A transient failure does not trigger a callback.\n- [ ] A filesystem operation that races with another process tolerates the\n      race (`fs.rmSync(path, {force: true})` or `try/catch`), not\n      `existsSync` + an unguarded call.\n- [ ] A failed lookup falls back to the previous behavior. It does not turn\n      a healthy state into an error state.\n- [ ] No synchronous process spawn or blocking call runs in a polling loop\n      or in a hot function.\n- [ ] `dispose()` releases every resource the object created, and runs for\n      objects that are already closed.\n- [ ] Event listeners added for one action are removed when the action ends.\n- [ ] Cached page-side state is refreshed before use. The page can reload\n      between tool calls.\n- [ ] Behavior shared by all tools that act on the selected page also runs\n      for tools defined with `defineTool`, not only `definePageTool`.\n- [ ] String and URL parsing handles userinfo, escaped delimiters, ports,\n      pattern groups, and empty or whitespace-only input. Use the full\n      authority, not a heuristic on the first or last character.\n- [ ] Truncation uses `Intl.Segmenter` graphemes, not `string.slice`.\n- [ ] Secrets never reach logs. Redact headers, URL userinfo, and query\n      strings in every log line, including process startup arguments.\n- [ ] A filesystem path parameter is validated with `validatePath()` even\n      when the value parses as a URL. The error says what the parameter\n      expects.\n- [ ] A value that crosses a process boundary (daemon IPC, CLI) is\n      serialized with `JSON.stringify` when it is not a primitive.\n- [ ] An identifier is issued only for items that a tool can resolve later.\n- [ ] A dedup key never collapses for items without identifying fields.\n      Use a counter or skip such items.\n- [ ] An error message names the exact condition that failed, with the\n      relevant values (pid, timeout, URL, parameter name).\n- [ ] A thrown non-`Error` value keeps its content in the error message.\n      Treat objects with a string `message` like `Error`.\n- [ ] Optional chaining and `?? default` are not applied to values whose\n      type is non-optional.\n- [ ] Version-dependent behavior (Node, Chrome, OS) is tested on every\n      version the `engines` field allows, or the test does not depend on it.\n\n## Tests\n\n- [ ] Every new or changed function has a test that calls it. A function\n      with no caller in `src/` and no test is removed from the PR.\n- [ ] Every review request of the form \"add a test for X\" has a test whose\n      name includes X.\n- [ ] A new test for a bug fails on `main` without the fix. Remove\n      assertions that pass with and without the fix.\n- [ ] A `notCalled` assertion targets the first method the handler calls\n      after the step under test, not a method inside a callback that a stub\n      never runs.\n- [ ] A test for a selected branch also asserts that the other branch was\n      not taken.\n- [ ] Assertions use `calledOnceWithExactly` or\n      `calledWithExactly(stub.nthCall, ...)`. Use `calledOnceWithMatch` only\n      for arguments that cannot be exact (for example an `AbortSignal`) or\n      when an overloaded signature forces `sinon.match.same`.\n- [ ] Options that the handler derives from its input are asserted, not\n      only the call count.\n- [ ] Test names describe the asserted behavior. Rename the test when the\n      assertion changes.\n- [ ] `if (value) { assert... }` does not appear in tests. Use\n      `assert.ok(value)` before the dependent assertions.\n- [ ] Real-browser tests exist only for behavior that mocks cannot observe.\n      A test file contains at most one slow end-to-end run per scenario.\n- [ ] A real-browser test that counts requests filters `favicon.ico`.\n- [ ] A real-browser test that waits for CDP events waits on the session or\n      result of the code under test, not on a new `createCDPSession()`.\n- [ ] Every awaited event in a test has a bounded timeout. Positive-path\n      tests poll for the condition instead of a fixed sleep.\n- [ ] New reusable stubs and fixtures live in `tests/mocks.ts` and build on\n      `sinon.createStubInstance`. No hand-rolled fixture uses `as unknown as`.\n- [ ] Mock options that equal the default value are not passed.\n- [ ] Temporary directories use `using dir = createTempDir(...)` from\n      `tests/utils.ts`, not `fs.mkdtemp`.\n- [ ] A test never reads the developer's home directory and never calls\n      `process.exit`. Stub locators and pass `exitProcess = false`.\n- [ ] When a real-browser test is replaced with mocks, each branch the old\n      test covered still has a test.\n- [ ] A test does not repeat a round-trip that another test already covers.\n      Stop after the first assertion that proves the change.\n\n## Documentation and output\n\n- [ ] A tool description states the exact behavior, including when a\n      parameter is ignored or depends on another parameter.\n- [ ] Skills stay generic. Add one short note in the tool-selection section\n      instead of a feature-specific workflow.\n- [ ] Inline comments are one sentence. Remove comments that repeat an\n      existing TSDoc.\n- [ ] Response text that depends on a condition prints only when the\n      condition is true.\n","plugin.json":"{\n  \"$schema\": \"https://agent-plugins.org/schemas/1.0.0/plugin.schema.json\",\n  \"name\": \"chrome-devtools\",\n  \"version\": \"1.12.0\",\n  \"description\": \"Reliable Chrome automation, debugging, and performance analysis for AI agents.\",\n  \"author\": {\n    \"name\": \"Google Chrome\",\n    \"url\": \"https://developer.chrome.com/\"\n  },\n  \"homepage\": \"https://developer.chrome.com/docs/devtools/agents\",\n  \"repository\": \"https://github.com/ChromeDevTools/chrome-devtools-mcp\",\n  \"license\": \"Apache-2.0\",\n  \"keywords\": [\n    \"browser\",\n    \"chrome\",\n    \"debugging\",\n    \"mcp\",\n    \"performance\"\n  ]\n}\n","skills/a11y-debugging/SKILL.md":"---\nname: a11y-debugging\ndescription: Uses Chrome DevTools MCP for accessibility (a11y) debugging and auditing based on web.dev guidelines. Use when testing semantic HTML, ARIA labels, focus states, keyboard navigation, tap targets, and color contrast.\n---\n\n## Core Concepts\n\n**Page Targeting**: Page-scoped tools (`take_snapshot`, `list_console_messages`, `evaluate_script`, `press_key`, `take_screenshot`, `lighthouse_audit`, etc.) require a `pageId` parameter. Retrieve available page IDs using `list_pages` or from `new_page`.\n\n**Accessibility Tree vs DOM**: Visually hiding an element (e.g., `CSS opacity: 0`) behaves differently for screen readers than `display: none` or `aria-hidden=\"true\"`. The `take_snapshot` tool returns the accessibility tree of the page, which represents what assistive technologies \"see\", making it the most reliable source of truth for semantic structure.\n\n**Reading web.dev documentation**: If you need to research specific accessibility guidelines (like `https://web.dev/articles/accessible-tap-targets`), you can append `.md.txt` to the URL (e.g., `https://web.dev/articles/accessible-tap-targets.md.txt`) to fetch the clean, raw markdown version. This is much easier to read!\n\n## Workflow Patterns\n\n### 1. Automated Audit (Lighthouse)\n\nStart by running a Lighthouse accessibility audit to get a comprehensive baseline. This tool provides a high-level score and lists specific failing elements with remediation advice.\n\n1.  Run the audit:\n    - Set `mode` to `\"navigation\"` to refresh the page and capture load issues.\n    - Omit `outputDirPath` to save reports to the default temporary directory (the exact path to `report.json` is returned under `Reports`), or set `outputDirPath` to a directory within the workspace.\n2.  **Analyze the Summary**:\n    - In the default text output, category scores use a 0-100 scale (100 is a perfect score), followed by `Passed: N` and `Failed: N` audit counts.\n    - If the server was started with `--experimentalStructuredContent`, inspect `summary.scores[].score` (0-1 scale) and `summary.audits.failed` in the structured content instead.\n3.  **Review the Report (CRITICAL)**:\n    - **Parsing**: Do not read the entire file line-by-line. Use a CLI tool like `jq` or a Node.js one-liner to filter for failures from the report path returned by `lighthouse_audit`:\n      ```bash\n      # Extract failing audits with their details (replace with report.json path from tool response)\n      node -e \"const r=require('<path/to/report.json>'); Object.values(r.audits).filter(a=>a.score!==null && a.score<1).forEach(a=>console.log(JSON.stringify({id:a.id, title:a.title, items:a.details?.items})))\"\n      ```\n    - This efficiently extracts the `selector` and `snippet` of failing elements without loading the full report into context.\n\n### 2. Browser Issues & Audits\n\nChrome automatically checks for common accessibility problems. Use `list_console_messages` to check for these native audits:\n\n- `types`: `[\"issue\"]`\n- `includePreservedMessages`: `true` (to catch issues that occurred during page load)\n\nThis often reveals missing labels, invalid ARIA attributes, and other critical errors without manual investigation.\n\n### 3. Semantics & Structure\n\nThe accessibility tree exposes the heading hierarchy and semantic landmarks.\n\n1.  Navigate to the page.\n2.  Use `take_snapshot` to capture the accessibility tree.\n3.  **Check Heading Levels**: Ensure heading levels (`h1`, `h2`, `h3`, etc.) are logical and do not skip levels. The snapshot will include heading roles.\n4.  **Content Reordering**: Verify that the DOM order (which drives the accessibility tree) matches the visual reading order. Use `take_screenshot` to inspect the visual layout and compare it against the snapshot structure to catch CSS floats or absolute positioning that jumbles the logical flow.\n\n### 4. Labels, Forms & Text Alternatives\n\n1.  Locate buttons, inputs, and images in the `take_snapshot` output.\n2.  Ensure interactive elements have an accessible name (e.g., a button should not just say `\"\"` if it only contains an icon).\n3.  **Orphaned Inputs**: Verify that all form inputs have associated labels. Use `evaluate_script` with the **\"Find Orphaned Form Inputs\" snippet** found in [references/a11y-snippets.md](references/a11y-snippets.md).\n4.  Check images for `alt` text.\n\n### 5. Focus & Keyboard Navigation\n\nTesting \"keyboard traps\" and proper focus management without visual feedback relies on tracking the focused element.\n\n1.  Use the `press_key` tool with `\"Tab\"` or `\"Shift+Tab\"` to move focus.\n2.  Use `take_snapshot` to capture the updated accessibility tree.\n3.  Locate the element marked as focused in the snapshot to verify focus moved to the expected interactive element.\n4.  If a modal opens, focus must move into the modal and \"trap\" within it until closed.\n\n### 6. Tap Targets and Visuals\n\nAccording to web.dev, tap targets should be at least 48x48 pixels with sufficient spacing. Since the accessibility tree doesn't show sizes, use `evaluate_script` with the **\"Measure Tap Target Size\" snippet** found in [references/a11y-snippets.md](references/a11y-snippets.md).\n\n_Pass the element's `uid` from the snapshot as an argument to `evaluate_script`._\n\n### 7. Color Contrast\n\nTo verify color contrast ratios, use the **\"Check Color Contrast\" snippet** found in [references/a11y-snippets.md](references/a11y-snippets.md) with `evaluate_script`.\n\n### 8. Global Page Checks\n\nVerify document-level accessibility settings often missed in component testing using the **\"Global Page Checks\" snippet** found in [references/a11y-snippets.md](references/a11y-snippets.md).\n\n## Troubleshooting\n\nIf standard a11y queries fail or the `evaluate_script` snippets return unexpected results:\n\n- **Visual Inspection**: If automated scripts cannot determine contrast (e.g., text over gradient images or complex backgrounds), use `take_screenshot` to capture the element. While models cannot measure exact contrast ratios from images, they can visually assess legibility and identify obvious issues.\n","skills/a11y-debugging/references/a11y-snippets.md":"# Accessibility Debugging Snippets\n\nUse these JavaScript snippets with the `evaluate_script` tool.\n\n## 1. Find Orphaned Form Inputs\n\nFinds form inputs that lack an associated label (no `label[for]`, `aria-label`, `aria-labelledby`, or wrapping `<label>`).\n\n```js\n() =>\n  Array.from(document.querySelectorAll('input, select, textarea'))\n    .filter(i => {\n      const hasId = i.id && document.querySelector(`label[for=\"${i.id}\"]`);\n      const hasAria =\n        i.getAttribute('aria-label') || i.getAttribute('aria-labelledby');\n      return !hasId && !hasAria && !i.closest('label');\n    })\n    .map(i => ({\n      tag: i.tagName,\n      id: i.id,\n      name: i.name,\n      placeholder: i.placeholder,\n    }));\n```\n\n## 2. Measure Tap Target Size\n\nReturns the bounding box dimensions of an element. Pass the element's `uid` from the snapshot as an argument to `evaluate_script`.\n\n```js\nel => {\n  const rect = el.getBoundingClientRect();\n  return {width: rect.width, height: rect.height};\n};\n```\n\n## 3. Check Color Contrast\n\nApproximates the contrast ratio between an element's text color and background color. Pass the element's `uid` to test against WCAG AA (4.5:1 for normal text, 3:1 for large text).\n\n**Note**: This uses a simplified algorithm and may not account for transparency, gradients, or background images. For production-grade auditing, consider injecting `axe-core`.\n\n```js\nel => {\n  function getRGB(colorStr) {\n    const match = colorStr.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/);\n    return match\n      ? [parseInt(match[1]), parseInt(match[2]), parseInt(match[3])]\n      : [255, 255, 255];\n  }\n  function luminance(r, g, b) {\n    const a = [r, g, b].map(function (v) {\n      v /= 255;\n      return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);\n    });\n    return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;\n  }\n\n  const style = window.getComputedStyle(el);\n  const fg = getRGB(style.color);\n  let bg = getRGB(style.backgroundColor);\n\n  const l1 = luminance(fg[0], fg[1], fg[2]);\n  const l2 = luminance(bg[0], bg[1], bg[2]);\n  const ratio = (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);\n\n  return {\n    color: style.color,\n    bg: style.backgroundColor,\n    contrastRatio: ratio.toFixed(2),\n  };\n};\n```\n\n## 4. Global Page Checks\n\nChecks document-level accessibility settings often missed in component testing.\n\n```js\n() => ({\n  lang:\n    document.documentElement.lang ||\n    'MISSING - Screen readers need this for pronunciation',\n  title: document.title || 'MISSING - Required for context',\n  viewport:\n    document.querySelector('meta[name=\"viewport\"]')?.content ||\n    'MISSING - Check for user-scalable=no (bad practice)',\n  reducedMotion: window.matchMedia('(prefers-reduced-motion: reduce)').matches\n    ? 'Enabled'\n    : 'Disabled',\n});\n```\n","skills/chrome-devtools-cli/SKILL.md":"---\nname: chrome-devtools-cli\ndescription: Use this skill to write shell scripts or run shell commands to automate tasks in the browser or otherwise use Chrome DevTools via CLI.\n---\n\nThe `chrome-devtools-mcp` CLI lets you interact with the browser from your terminal.\n\n## Setup\n\n_Note: If this is your very first time using the CLI, see [references/installation.md](references/installation.md) for setup. Installation is a one-time prerequisite and is **not** part of the regular AI workflow._\n\n## AI Workflow\n\n1. **Execute**: Run tools directly. If you don't know the target page's ID, run `chrome-devtools list_pages` to find it. The background server starts implicitly; **do not** run `start`/`status`/`stop` before each use.\n2. **Inspect**: Use `chrome-devtools take_snapshot <pageId>` to get an element `<uid>`.\n3. **Act**: Use `chrome-devtools click <pageId> <uid>`, `chrome-devtools fill <pageId> <uid> <value>`, etc. State persists across commands.\n\nSnapshot example:\n\n```\nuid=1_0 RootWebArea \"Example Domain\" url=\"https://example.com/\"\n  uid=1_1 heading \"Example Domain\" level=\"1\"\n```\n\n## Permissions & File Access\n\nBy default, the CLI has full filesystem access (`--allowUnrestrictedPaths=true`), allowing file-saving parameters (`--filePath`, `--outputDirPath`) and `upload_file` to access files anywhere on the system. Pass `--allowUnrestrictedPaths=false` if you want to restrict file access to the OS temp directory.\n\n## Command Usage\n\n```sh\nchrome-devtools <tool> [arguments] [flags]\n```\n\n- Required arguments are passed positionally; optional arguments use flags.\n- Use `--help` on any command for usage details.\n- Output defaults to plain Markdown-like text; pass `--output-format=json` for JSON.\n\n## Input Automation (<uid> from snapshot)\n\n```bash\nchrome-devtools take_snapshot 1 # Take a text snapshot of the page to get UIDs for elements\nchrome-devtools click 1 \"id\" # Clicks on the provided element\nchrome-devtools click 1 \"id\" --dblClick true --includeSnapshot true # Double clicks and returns a snapshot\nchrome-devtools drag 1 \"src\" \"dst\" # Drag an element onto another element\nchrome-devtools drag 1 \"src\" \"dst\" --includeSnapshot true # Drag an element and return a snapshot\nchrome-devtools fill 1 \"id\" \"text\" # Type text into an input, textarea, or select an option\nchrome-devtools fill 1 \"id\" \"text\" --includeSnapshot true # Fill an element and return a snapshot\nchrome-devtools handle_dialog 1 accept # Handle a browser dialog (accept/dismiss)\nchrome-devtools handle_dialog 1 dismiss --promptText \"hi\" # Dismiss a dialog with prompt text\nchrome-devtools hover 1 \"id\" # Hover over the provided element\nchrome-devtools hover 1 \"id\" --includeSnapshot true # Hover over an element and return a snapshot\nchrome-devtools press_key 1 \"Enter\" # Press a key or key combination (\"Control+A\", \"Escape\")\nchrome-devtools press_key 1 \"Control+A\" --includeSnapshot true # Press a key and return a snapshot\nchrome-devtools type_text 1 \"hello\" # Type text using keyboard into a focused input\nchrome-devtools type_text 1 \"hello\" --submitKey \"Enter\" # Type text and press a submit key\nchrome-devtools upload_file 1 \"id\" \"file.txt\" # Upload a file through a provided element\nchrome-devtools upload_file 1 \"id\" \"file.txt\" --includeSnapshot true # Upload a file and return a snapshot\n```\n\n## Navigation\n\n```bash\nchrome-devtools close_page 1 # Closes the page by its index\nchrome-devtools list_pages # Get a list of pages open in the browser\nchrome-devtools navigate_page 1 --url \"https://example.com\" # Navigates the currently selected page to a URL\nchrome-devtools navigate_page 1 --type \"reload\" --ignoreCache true # Reload page ignoring cache\nchrome-devtools navigate_page 1 --url \"https://example.com\" --timeout 5000 # Navigate with a timeout\nchrome-devtools navigate_page 1 --type \"reload\" --handleBeforeUnload \"accept\" # Handle before unload dialog\nchrome-devtools navigate_page 1 --type \"back\" --initScript \"foo()\" # Navigate back and run an init script\nchrome-devtools new_page \"https://example.com\" # Creates a new page\nchrome-devtools new_page \"https://example.com\" --background true --timeout 5000 # Create new page in background\nchrome-devtools new_page \"https://example.com\" --isolatedContext \"ctx\" # Create new page with isolated context\nchrome-devtools select_page 1 # Select a page as a context for future tool calls\nchrome-devtools select_page 1 --bringToFront true # Select a page and bring it to front\n```\n\n## Emulation\n\nEach `emulate` call replaces all previously configured emulation settings (except `--extraHttpHeaders`), so pass all desired settings in a single call.\n\n```bash\nchrome-devtools emulate 1 --networkConditions \"Offline\" # Emulate network conditions (resets other settings)\nchrome-devtools emulate 1 --cpuThrottlingRate 4 --geolocation \"0,0\" # Emulate CPU throttling and geolocation (resets other settings)\nchrome-devtools emulate 1 --colorScheme \"dark\" --viewport \"1920x1080\" # Emulate color scheme and viewport (resets other settings)\nchrome-devtools emulate 1 --userAgent \"Mozilla/5.0...\" # Emulate user agent (resets other settings)\nchrome-devtools resize_page 1 1920 1080 # Resizes the selected page's window\n```\n\n## Performance\n\n```bash\nchrome-devtools performance_analyze_insight 1 \"NAVIGATION_0\" \"LCPBreakdown\" # Get more details on a specific Performance Insight (pageId, insightSetId, insightName)\nchrome-devtools performance_start_trace 1 --reload true --autoStop false # Starts a performance trace recording (reload, autoStop)\nchrome-devtools performance_start_trace 1 --reload true --autoStop true --filePath \"t.json.gz\" # Start trace and save to a file\nchrome-devtools performance_stop_trace 1 # Stops the active performance trace\nchrome-devtools performance_stop_trace 1 --filePath \"t.json.gz\" # Stop trace and save to a file\n```\n\n## Memory\n\n```bash\nchrome-devtools take_heapsnapshot 1 \"./snap.heapsnapshot\" # Capture a memory heap snapshot\n```\n\n### Memory Debugging (requires `--memoryDebugging=true`)\n\n```bash\nchrome-devtools get_heapsnapshot_summary \"./snap.heapsnapshot\" # Get snapshot summary stats\nchrome-devtools compare_heapsnapshots \"./base.heapsnapshot\" \"./target.heapsnapshot\" # Compare two snapshots\nchrome-devtools get_heapsnapshot_details \"./snap.heapsnapshot\" # Snapshot statistics and per-class aggregates (with class IDs)\nchrome-devtools get_heapsnapshot_class_nodes \"./snap.heapsnapshot\" 18 # Inspect instances of a class (class ID from get_heapsnapshot_details)\nchrome-devtools get_heapsnapshot_dominators \"./snap.heapsnapshot\" 123 # Dominator tree for node\nchrome-devtools get_heapsnapshot_duplicate_strings \"./snap.heapsnapshot\" # Find duplicated strings\nchrome-devtools get_heapsnapshot_edges \"./snap.heapsnapshot\" 123 # Node edges/references\nchrome-devtools get_heapsnapshot_object_details \"./snap.heapsnapshot\" 123 # Object details by node ID\nchrome-devtools get_heapsnapshot_retainers \"./snap.heapsnapshot\" 123 # Retaining objects\nchrome-devtools get_heapsnapshot_retaining_paths \"./snap.heapsnapshot\" 123 # Shortest retaining paths\nchrome-devtools close_heapsnapshot \"./snap.heapsnapshot\" # Free memory from loaded snapshot\n```\n\n## Network\n\n```bash\nchrome-devtools get_network_request 1 # Get the currently selected network request for page 1\nchrome-devtools get_network_request 1 --reqid 1 --requestFilePath \"req.md\" # Get request by id and save to file\nchrome-devtools get_network_request 1 --reqid 1 --responseFilePath \"res.md\" # Save response body to file\nchrome-devtools list_network_requests 1 # List all network requests for page 1\nchrome-devtools list_network_requests 1 --pageSize 50 --pageIdx 0 # List network requests with pagination\nchrome-devtools list_network_requests 1 --resourceTypes fetch # Filter requests by resource type\nchrome-devtools list_network_requests 1 --includePreservedRequests true # Include preserved requests\n```\n\n## Debugging & Inspection\n\n```bash\nchrome-devtools evaluate_script \"() => document.title\" --pageId 1 # Evaluate a JavaScript function on page 1\nchrome-devtools evaluate_script \"(a) => a.innerText\" --pageId 1 --args 1_4 # Evaluate JS with UID arguments on page 1\nchrome-devtools evaluate_script --pageId 1 --sourcePath /path/to/script.js --format script # Evaluate a local classic JavaScript file on page 1\nchrome-devtools get_console_message 1 1 # Gets a console message by its ID\nchrome-devtools get_css_styles 1 \"1_4\" # Get CSS styles with pagination on page 1 (default: 10 rules, pageIdx 0)\nchrome-devtools get_css_styles 1 \"1_4\" --pageSize 20 --pageIdx 1 # Paginate CSS rules with custom page size and custom 0-based page index\nchrome-devtools lighthouse_audit 1 --mode \"navigation\" # Run Lighthouse audit for navigation\nchrome-devtools lighthouse_audit 1 --mode \"snapshot\" --device \"mobile\" # Run Lighthouse audit for a snapshot on mobile\nchrome-devtools lighthouse_audit 1 --outputDirPath ./out # Run Lighthouse audit and save reports\nchrome-devtools list_console_messages 1 # List all console messages\nchrome-devtools list_console_messages 1 --pageSize 20 --pageIdx 1 # List console messages with pagination\nchrome-devtools list_console_messages 1 --types error --types info # Filter console messages by type\nchrome-devtools list_console_messages 1 --includePreservedMessages true # Include preserved messages\nchrome-devtools take_screenshot 1 # Take a screenshot of the page viewport\nchrome-devtools take_screenshot 1 --fullPage true --format \"jpeg\" --quality 80 # Take a full page screenshot as JPEG with quality\nchrome-devtools take_screenshot 1 --uid \"id\" --filePath \"s.png\" # Take a screenshot of an element\nchrome-devtools take_snapshot 1 # Take a text snapshot of the page from the a11y tree\nchrome-devtools take_snapshot 1 --verbose true --filePath \"s.txt\" # Take a verbose snapshot and save to file\nchrome-devtools screencast_start 1 --filePath \"screen.mp4\" # Starts an mp4 screencast recording on page 1\nchrome-devtools screencast_stop 1 # Stops the active screencast on page 1\n```\n\n## Extensions\n\n```bash\nchrome-devtools list_extensions # Lists all the Chrome extensions installed in the browser\nchrome-devtools install_extension \"/path/to/extension\" # Installs a Chrome extension from the given path\nchrome-devtools uninstall_extension \"extension_id\" # Uninstalls a Chrome extension by its ID\nchrome-devtools reload_extension \"extension_id\" # Reloads an unpacked Chrome extension by its ID\nchrome-devtools trigger_extension_action \"extension_id\" # Triggers the default action of an extension by its ID\n```\n\n## Progressive Web Apps (requires `--categoryPwa=true`)\n\n```bash\nchrome-devtools install_pwa \"https://example.com/\" \"https://example.com/\" # Install PWA by manifest ID and install URL\nchrome-devtools launch_pwa \"https://example.com/\" # Launch installed PWA\nchrome-devtools get_os_app_state \"https://example.com/\" # Get OS app installation state\nchrome-devtools uninstall_pwa \"https://example.com/\" # Uninstall PWA and close windows\n```\n\n## Experimental Features\n\nExperimental tools are disabled by default. Enable them with the corresponding flag during `start`.\n\n```bash\nchrome-devtools click_at 1 100 200 # Clicks at the provided coordinates on page 1 (requires --experimentalVision=true)\nchrome-devtools list_webmcp_tools 1 # List all WebMCP tools on page 1 (requires --categoryExperimentalWebmcp=true)\nchrome-devtools execute_webmcp_tool 1 \"tool_name\" --input '{\"arg\":\"val\"}' # Execute a WebMCP tool on page 1 (requires --categoryExperimentalWebmcp=true)\nchrome-devtools list_3p_developer_tools 1 # List third-party developer tools on page 1 (requires --categoryExperimentalThirdParty=true)\nchrome-devtools execute_3p_developer_tool 1 \"tool_name\" --params '{\"arg\":\"val\"}' # Execute third-party developer tool on page 1 (requires --categoryExperimentalThirdParty=true)\n```\n\n## Service Management\n\n```bash\nchrome-devtools start   # Start or restart chrome-devtools-mcp\nchrome-devtools start --headless=false # Start with visible browser window\nchrome-devtools status  # Checks if chrome-devtools-mcp is running\nchrome-devtools stop    # Stop chrome-devtools-mcp if any\n```\n","skills/chrome-devtools-cli/references/installation.md":"# Installation\n\nInstall the package globally to make the `chrome-devtools` command available. You only need to do this the first time you use it.\n\n```sh\nnpm i chrome-devtools-mcp@latest -g\nchrome-devtools status # check if install worked.\n```\n\n## Troubleshooting\n\n- **Command not found:** If `chrome-devtools` is not recognized, ensure your global npm `bin` directory is in your system's `PATH`. Restart your terminal or source your shell configuration file (e.g., `.bashrc`, `.zshrc`).\n- **Permission errors:** If you encounter `EACCES` or permission errors during installation, avoid using `sudo`. Instead, use a node version manager like `nvm`, or configure npm to use a different global directory.\n- **Old version running:** Run `chrome-devtools stop && npm uninstall -g chrome-devtools-mcp` before reinstalling, or ensure the latest version is being picked up by your path.\n","skills/chrome-devtools/SKILL.md":"---\nname: chrome-devtools\ndescription: Uses Chrome DevTools via MCP for efficient debugging, troubleshooting and browser automation. Use when debugging web pages, automating browser interactions, analyzing performance, or inspecting network requests. This skill does not apply to `--slim` mode (MCP configuration).\n---\n\n## Core Concepts\n\n**Browser lifecycle**: Browser starts automatically on first tool call using a persistent Chrome profile. Configure via CLI args in the MCP server configuration: `npx chrome-devtools-mcp@latest --help`.\nAddional tooling can be enabled by providing the following flags:\n\n- For extension tooling, use the `--categoryExtensions` flag.\n- For memory tooling, use the `--memoryDebugging` flag.\n\n**Page targeting**: Page-scoped tools require a `pageId` parameter to target a specific page. Use `list_pages` to see available pages and their IDs (e.g. `pageId: 1`), or use the ID returned when creating a page with `new_page`.\nNote: For `evaluate_script`, `pageId` is required when targeting pages. However, when `--categoryExtensions` is enabled, `pageId` is optional so you can pass `serviceWorkerId` instead to evaluate inside an extension background service worker.\n**Element interaction**: Use `take_snapshot` to get page structure with element `uid`s. Each element has a unique `uid` for interaction. If an element isn't found, take a fresh snapshot - the element may have been removed or the page changed.\n\n## Workflow Patterns\n\n### Before interacting with a page\n\n1. Navigate: `navigate_page` or `new_page`\n2. Wait: `wait_for` to ensure content is loaded if you know what you look for.\n3. Snapshot: `take_snapshot` with `pageId` to understand page structure\n4. Interact: Use element `uid`s from snapshot for `click`, `fill`, etc., passing the corresponding `pageId`.\n\n### Efficient data retrieval\n\n- Use `filePath` parameter for large outputs (screenshots, snapshots, traces)\n- Use pagination (`pageIdx`, `pageSize`) and filtering (`types`) to minimize data\n- Set `includeSnapshot: false` on input actions unless you need updated page state\n\n### Tool selection\n\n- **Automation/interaction**: `take_snapshot` (text-based, faster, better for automation)\n- **Visual inspection**: `take_screenshot` (when user needs to see visual state)\n- **CSS & Styling inspection**: Use `get_css_styles` to inspect matched rules, cascade, and CSS variables. Treat the output as authoritative and complete.\n- **Additional details**: Use `evaluate_script` for runtime DOM/JS data not available via `get_css_styles` or `take_snapshot`.\n\n### Parallel execution\n\nYou can send multiple tool calls in parallel, but maintain correct order: navigate → wait → snapshot → interact.\n\n### Testing an extension\n\n> **Before proceeding**: Extension tools (`install_extension`, `list_extensions`, etc.) are only available when the MCP server is started with the `--categoryExtensions` flag. If these tools are not in your tool list, stop and ask the user to update their MCP server configuration:\n>\n> ```json\n> {\n>   \"mcpServers\": {\n>     \"chrome-devtools\": {\n>       \"command\": \"npx\",\n>       \"args\": [\"chrome-devtools-mcp@latest\", \"--categoryExtensions\"]\n>     }\n>   }\n> }\n> ```\n>\n> After updating, the user must restart the MCP server (or their AI client) for the change to take effect.\n\n1. **Install**: Use `install_extension` with the path to the unpacked extension.\n2. **Identify**: Get the extension ID from the response or by calling `list_extensions`.\n3. **Trigger Action**: Use `trigger_extension_action` to open the popup or side panel if applicable.\n4. **Verify Service Worker**: Use `evaluate_script` with `serviceWorkerId` (omitting `pageId` and `args`) to check extension state or trigger background actions. When evaluating in a page, pass `pageId` (omitting `serviceWorkerId`).\n5. **Verify Page Behavior**: Navigate to a page where the extension operates and use `take_snapshot` to check if content scripts injected elements or modified the page correctly.\n\n## Troubleshooting\n\nIf `chrome-devtools-mcp` is insufficient, guide users to use Chrome DevTools UI:\n\n- https://developer.chrome.com/docs/devtools\n- https://developer.chrome.com/docs/devtools/ai-assistance\n\nIf there are errors launching `chrome-devtools-mcp` or Chrome, refer to https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/troubleshooting.md.\n","skills/cookie-debugging/SKILL.md":"---\nname: cookie-debugging\ndescription: Uses Chrome DevTools MCP for inspecting, debugging, and testing cookies, session state, authentication issues, and cookie consent compliance. Use when diagnosing 401/403 errors, authentication redirects, session expiration, Cookie/Set-Cookie header issues, cookie banner consent conformance, or third-party cookie/SameSite/Partitioned cookie warnings.\n---\n\n## Core Concepts\n\n**Page Targeting**: Page-scoped tools (`list_network_requests`, `get_network_request`, `list_console_messages`, `evaluate_script`, `navigate_page`, `take_snapshot`, `click`, `lighthouse_audit`, `close_page`, etc.) require a `pageId` parameter. Retrieve available page IDs using `list_pages` or from `new_page`.\n\n### HttpOnly vs Client-Side Storage\n\nCookies marked `HttpOnly` cannot be accessed or modified by client-side JavaScript (`cookieStore` or `document.cookie`). However, the browser **automatically attaches active HttpOnly cookies to outgoing HTTP request headers (`Cookie`)**.\n\n- To inspect current `HttpOnly` values: Look at the `Cookie` request header of any outgoing HTTP request via `get_network_request`.\n- To inspect how cookies were created or configured: Look at the `Set-Cookie` response header of login/auth responses.\n- To inspect non-`HttpOnly` cookies: Use `evaluate_script` with the modern `cookieStore` API (`async () => await cookieStore.getAll()`).\n\n### Session Strategy: Live Tab vs Isolated Context\n\nChoose the right session environment to avoid state contamination (e.g., residual analytics or auth tokens):\n\n| Strategy                            | When to Use                                                                   | Setup / Teardown                                                                                              |\n| :---------------------------------- | :---------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------ |\n| **Live Tab (Active Page)**          | Diagnosing an active user session, live 401/403 error, or current state.      | Operates on the user's existing page; get its `pageId` from `list_pages`.                                     |\n| **Clean-Slate (`isolatedContext`)** | Testing cookie consent banners, first-time visits, or zero-cookie guarantees. | Call `new_page` with a unique `isolatedContext` (e.g. `\"consent-audit-1\"`). When finished, call `close_page`. |\n\n### Client-Side Capabilities & Limitations\n\n| Action                                                           | Client JavaScript (`cookieStore` / `document.cookie`) | DevTools Network & Context Tools                        |\n| :--------------------------------------------------------------- | :---------------------------------------------------- | :------------------------------------------------------ |\n| **Read Non-HttpOnly**                                            | ✅ `async () => await cookieStore.getAll()`           | ✅ `get_network_request` (Request `Cookie`)             |\n| **Read HttpOnly**                                                | ❌ Blocked by browser security                        | ✅ `get_network_request` (Request `Cookie`)             |\n| **Inspect Attributes** (`Domain`, `Path`, `SameSite`, `Expires`) | ✅ `async () => await cookieStore.getAll()`           | ✅ `get_network_request` (Response `Set-Cookie`)        |\n| **Modify / Delete Non-HttpOnly**                                 | ✅ `async () => await cookieStore.set(...)`           | N/A                                                     |\n| **Modify / Delete HttpOnly**                                     | ❌ **Silent failure** in JavaScript                   | ✅ Use `new_page(isolatedContext: ...)` for clean state |\n\n> [!WARNING]\n> Attempting to clear an `HttpOnly` cookie via JavaScript (`cookieStore.delete` or `document.cookie = \"...; max-age=0\"`) will silently fail. To test in an unauthenticated or fresh state, always spawn a new isolated context using `new_page` with `isolatedContext`.\n\n---\n\n## Workflow Patterns\n\n### 1. Diagnosing Authentication Failures & Redirects (401 / 403)\n\nWhen an authenticated page request fails, returns 401/403, or redirects to login:\n\n1. **List Recent Requests**: Call `list_network_requests` with `includePreservedRequests: true`.\n2. **Find the Target Request**: Locate the failing request (401/403) or redirect (302/307).\n3. **Inspect Outgoing `Cookie` Header**: Call `get_network_request` with the `reqid`.\n   - Verify if the `Cookie` header was attached and whether required tokens (e.g. `SESSION_ID`, `auth_token`) were sent.\n4. **Trigger Active Inspection (If no recent request exists)**:\n   - If the cookie was set in a previous session and no network call is listed, trigger a request:\n     - Use `navigate_page` with `type: \"reload\"`, OR\n     - Call `evaluate_script` with `() => fetch(window.location.href)`\n   - Then call `get_network_request` on the new request to inspect the active `Cookie` header.\n5. **Trace the Setting Request**: If the cookie is missing or rejected:\n   - Check earlier login/handshake responses for `Set-Cookie` directives:\n     - **Path mismatch**: e.g., `Path=/api` when the request is to `/`.\n     - **Domain mismatch**: e.g., `Domain=api.example.com` preventing cookies on `sub.example.com`.\n     - **Secure flag on HTTP**: `Secure` cookies are never sent over unencrypted `http://`.\n     - **SameSite blocking**: `SameSite=Strict` cookies are omitted on cross-site navigations.\n     - **Expiration**: Check if `Expires` or `Max-Age` elapsed.\n\n### 2. Cookie Banner & Consent Conformance Testing\n\nTo verify that no non-essential or tracking cookies are set before consent or when declining:\n\n1. **Start Clean**: Open a fresh isolated context with a dedicated name:\n   ```json\n   {\"url\": \"<PAGE_URL>\", \"isolatedContext\": \"consent-test-1\"}\n   ```\n2. **Record Baseline Cookies**: Before interacting with the banner, run `evaluate_script` with `async () => await cookieStore.getAll()`.\n3. **Inspect Premature Network Requests & Issues**:\n   - Call `list_network_requests` to ensure no third-party tracking beacons fired before consent.\n   - Call `list_console_messages` with `types: [\"issue\"]` to check for tracking warnings.\n4. **Interact with Consent Banner**:\n   - Capture snapshot with `take_snapshot` to locate the \"Decline\" or \"Reject All\" button `uid`.\n   - Click the button with `click`.\n5. **Verify Cookie Difference**:\n   - Run `evaluate_script` with `async () => await cookieStore.getAll()` after clicking to assert that only strictly necessary or consent-state cookies exist.\n6. **Test Consent Revocation (Lifecycle Audit)**:\n   - When auditing consent withdrawal or preference changes:\n     - Locate and click the \"Cookie Settings\", \"Manage Preferences\", or footer privacy trigger (`take_snapshot` $\\rightarrow$ `click`).\n     - Deselect non-essential categories or click \"Revoke All\" / \"Save Preferences\".\n     - Re-query `cookieStore.getAll()` to verify previously accepted non-essential cookies were cleared or expired.\n     - Call `list_network_requests` on subsequent actions to ensure tracking beacons are no longer fired.\n7. **Teardown Context**: Call `close_page` when the audit is complete to prevent leftover cookies from affecting subsequent tasks.\n\n### 3. Auditing Cookie Security, SameSite & CHIPS (Partitioned Cookies)\n\n1. **Fast-Track: Native DevTools Issues (Recommended)**:\n   - Call `list_console_messages` with:\n     ```json\n     {\n       \"pageId\": 1,\n       \"types\": [\"issue\"],\n       \"includePreservedMessages\": true\n     }\n     ```\n   - Check for `CookieIssue` entries, such as:\n     - `SameSiteNoneInsecure`: `SameSite=None` without `Secure`.\n     - `ThirdPartyCookiePhaseout`: Third-party cookie blocked or restricted.\n     - `SchemefulSameSite`: Cross-scheme cookie issues.\n     - `PartitionedCookies`: Invalid CHIPS partitioning attributes.\n2. **Deep Audit: Lighthouse Third-Party Cookies**:\n   - Run `lighthouse_audit` with `mode: \"navigation\"` (omit `outputDirPath` to use the default temporary files, or specify a directory within the workspace).\n   - **Extract the specific cookie audit** from the returned `report.json` path without loading the full report into context:\n     ```bash\n     node -e \"const r=require('<path/to/report.json>'); const a=r.audits['third-party-cookies']; console.log(JSON.stringify({score: a?.score, displayValue: a?.displayValue, items: a?.details?.items}))\"\n     ```\n\n### 4. Client-Side Cookie Inspection & Manipulation\n\nFor client-accessible, non-`HttpOnly` cookies (e.g., UI preferences, non-sensitive feature flags):\n\n1. **Read Cookies & Attributes**:\n   - Use the modern asynchronous Cookie Store API:\n     ```js\n     async () => await cookieStore.getAll();\n     ```\n   - _Fallback for insecure HTTP origins_: `() => document.cookie`.\n2. **Set / Modify Cookie**:\n   - Set client cookie via `cookieStore`:\n     ```js\n     async () =>\n       await cookieStore.set({\n         name: 'theme',\n         value: 'dark',\n         expires: Date.now() + 86400000,\n         sameSite: 'lax',\n       });\n     ```\n3. **Delete Cookie**:\n   - Clear client cookie:\n     ```js\n     async () => await cookieStore.delete('theme');\n     ```\n\n---\n\n## Troubleshooting\n\n- **`cookieStore` is undefined**: `cookieStore` requires a Secure Context (`https://`, `localhost`, or `127.0.0.1`). On non-secure HTTP origins, use `() => document.cookie` or test over HTTPS.\n- **`evaluate_script` returns empty / unresolved Promise**: `cookieStore` methods are asynchronous. Always wrap calls with `async () => await cookieStore.getAll()`.\n- **Cookie not visible in JavaScript**: The cookie is marked `HttpOnly`. Trigger a network request and call `get_network_request` to view it in the `Cookie` request header.\n- **JavaScript deletion did not remove cookie**: The cookie is `HttpOnly` or requires matching `Path` and `Domain` parameters. Use a fresh `isolatedContext` with `new_page` for a clean slate.\n- **Cookie set in response but not sent in requests**:\n  - Verify if page is `http://` while cookie specifies `Secure`.\n  - Check if `Domain` restricts subdomains.\n  - Check `list_console_messages(types: [\"issue\"])` for browser rejection reasons.\n- **Residual cookies contaminating audits**: Always use `new_page` with a unique `isolatedContext` when running compliance tests, and call `close_page` when done.\n","skills/debug-optimize-lcp/SKILL.md":"---\nname: debug-optimize-lcp\ndescription: Guides debugging and optimizing Largest Contentful Paint (LCP) using Chrome DevTools MCP tools. Use this skill whenever the user asks about LCP performance, slow page loads, Core Web Vitals optimization, or wants to understand why their page's main content takes too long to appear. Also use when the user mentions \"largest contentful paint\", \"page load speed\", \"CWV\", or wants to improve how fast their hero image or main content renders.\n---\n\n## What is LCP and why it matters\n\nLargest Contentful Paint (LCP) measures how quickly a page's main content becomes visible. It's the time from navigation start until the largest image or text block renders in the viewport.\n\n- **Good**: 2.5 seconds or less\n- **Needs improvement**: 2.5–4.0 seconds\n- **Poor**: greater than 4.0 seconds\n\nLCP is a Core Web Vital that directly affects user experience and search ranking. On 73% of mobile pages, the LCP element is an image.\n\n## LCP Subparts Breakdown\n\nEvery page's LCP breaks down into four sequential subparts with no gaps or overlaps. Understanding which subpart is the bottleneck is the key to effective optimization.\n\n| Subpart                       | Ideal % of LCP | What it measures                               |\n| ----------------------------- | -------------- | ---------------------------------------------- |\n| **Time to First Byte (TTFB)** | ~40%           | Navigation start → first byte of HTML received |\n| **Resource load delay**       | <10%           | TTFB → browser starts loading the LCP resource |\n| **Resource load duration**    | ~40%           | Time to download the LCP resource              |\n| **Element render delay**      | <10%           | LCP resource downloaded → LCP element rendered |\n\nThe \"delay\" subparts should be as close to zero as possible. If either delay subpart is large relative to the total LCP, that's the first place to optimize.\n\n**Common Pitfall**: Optimizing one subpart (like compressing an image to reduce load duration) without checking others. If render delay is the real bottleneck, a smaller image won't help — the saved time just shifts to render delay.\n\n## Debugging Workflow\n\nFollow these steps in order. Each step builds on the previous one.\n\n### Step 1: Record a Performance Trace\n\nNavigate to the page, then record a trace with reload to capture the full page load including LCP:\n\n1. `navigate_page` with `pageId` to the target URL.\n2. `performance_start_trace` with `pageId`, `reload: true` and `autoStop: true`.\n\nThe trace results will include LCP timing and available insight sets. Note the insight set IDs from the output — you'll need them in the next step.\n\n### Step 2: Analyze LCP Insights\n\nUse `performance_analyze_insight` to drill into LCP-specific insights. Look for these insight names in the trace results:\n\n- **LCPBreakdown** — Shows the four LCP subparts with timing for each.\n- **DocumentLatency** — Server response time issues affecting TTFB.\n- **RenderBlocking** — Resources blocking the LCP element from rendering.\n- **LCPDiscovery** — Whether the LCP resource was discoverable early.\n\nCall `performance_analyze_insight` with `pageId`, the insight set ID, and the insight name from the trace results.\n\n### Step 3: Identify the LCP Element\n\nUse `evaluate_script` (with `pageId`) and the **\"Identify LCP Element\" snippet** found in [references/lcp-snippets.md](references/lcp-snippets.md) to reveal the LCP element's tag, resource URL, and raw timing data.\n\nThe `url` field tells you what resource to look for in the network waterfall. If `url` is empty, the LCP element is text-based (no resource to load).\n\n### Step 4: Check the Network Waterfall\n\nUse `list_network_requests` to see when the LCP resource loaded relative to other resources:\n\n- Call `list_network_requests` with `pageId` filtered by `resourceTypes: [\"image\", \"font\"]` (adjust based on Step 3).\n- Then use `get_network_request` with `pageId` and the LCP resource's request ID for full details.\n\n**Key Checks:**\n\n- **Start Time**: Compare against the HTML document and the first resource. If the LCP resource starts much later than the first resource, there's resource load delay to eliminate.\n- **Duration**: A large resource load duration suggests the file is too big or the server is slow.\n\n### Step 5: Inspect HTML for Common Issues\n\nUse `evaluate_script` (with `pageId`) and the **\"Audit Common Issues\" snippet** found in [references/lcp-snippets.md](references/lcp-snippets.md) to check for lazy-loaded images in the viewport, missing fetchpriority, and render-blocking scripts.\n\n## Optimization Strategies\n\nAfter identifying the bottleneck subpart, apply these prioritized fixes.\n\n### 1. Eliminate Resource Load Delay (target: <10%)\n\nThe most common bottleneck. The LCP resource should start loading immediately.\n\n- **Root Cause**: LCP image loaded via JS/CSS, `data-src` usage, or `loading=\"lazy\"`.\n- **Fix**: Use standard `<img>` with `src`. **Never** lazy-load the LCP image.\n- **Fix**: Add `<link rel=\"preload\" fetchpriority=\"high\">` if the image isn't discoverable in HTML.\n- **Fix**: Add `fetchpriority=\"high\"` to the LCP `<img>` tag.\n\n### 2. Eliminate Element Render Delay (target: <10%)\n\nThe element should render immediately after loading.\n\n- **Root Cause**: Large stylesheets, synchronous scripts in `<head>`, or main thread blocking.\n- **Fix**: Inline critical CSS, defer non-critical CSS/JS.\n- **Fix**: Break up long tasks blocking the main thread.\n- **Fix**: Use Server-Side Rendering (SSR) so the element exists in initial HTML.\n\n### 3. Reduce Resource Load Duration (target: ~40%)\n\nMake the resource smaller or faster to deliver.\n\n- **Fix**: Use modern formats (WebP, AVIF) and responsive images (`srcset`).\n- **Fix**: Serve from a CDN.\n- **Fix**: Set `Cache-Control` headers.\n- **Fix**: Use `font-display: swap` if LCP is text blocked by a web font.\n\n### 4. Reduce TTFB (target: ~40%)\n\nThe HTML document itself takes too long to arrive.\n\n- **Fix**: Minimize redirects and optimize server response time.\n- **Fix**: Cache HTML at the edge (CDN).\n- **Fix**: Ensure pages are eligible for back/forward cache (bfcache).\n\n## Verifying Fixes & Emulation\n\n- **Verification**: Re-run the trace (`performance_start_trace` with `pageId` and `reload: true`) and compare the new subpart breakdown. The bottleneck should shrink.\n- **Emulation**: Lab measurements differ from real-world experience. Use `emulate` to test under constraints:\n  - `emulate` with `pageId`, `networkConditions: \"Fast 3G\"` and `cpuThrottlingRate: 4`.\n  - This surfaces issues visible only on slower connections/devices.\n","skills/debug-optimize-lcp/references/elements-and-size.md":"# Elements and Size for LCP\n\n## What Elements are Considered?\n\nThe types of elements considered for Largest Contentful Paint (LCP) are:\n\n- **`<img>` elements**: The first frame presentation time is used for animated content like GIFs.\n- **`<image>` elements** inside an `<svg>` element.\n- **`<video>` elements**: The poster image load time or first frame presentation time, whichever is earlier.\n- **Background images**: Elements with a background image loaded using `url()`.\n- **Block-level elements**: Containing text nodes or other inline-level text element children.\n\n## Heuristics to Exclude Non-Contentful Elements\n\nChromium-based browsers use heuristics to exclude:\n\n- Elements with **opacity of 0**.\n- Elements that **cover the full viewport** (likely background).\n- **Placeholder images** or low-entropy images.\n\n## How is an Element's Size Determined?\n\n- **Visible Area**: Typically the size visible within the viewport. Extending outside, clipped, or overflow portions don't count.\n- **Image Elements**: Either the visible size or the intrinsic size, whichever is smaller.\n- **Text Elements**: The smallest rectangle containing all text nodes.\n- **Exclusions**: Margin, padding, and borders are not considered toward the size.\n- **Containment**: Every text node belongs to its closest block-level ancestor element.\n","skills/debug-optimize-lcp/references/lcp-breakdown.md":"# Largest Contentful Paint (LCP) Breakdown\n\nLCP measures the time from when the user initiates loading the page until the largest image or text block is rendered within the viewport. To provide a good user experience, sites should strive to have an LCP of 2.5 seconds or less for at least 75% of page visits.\n\n## The Four Subparts of LCP\n\nEvery page's LCP consists of these four subcategories. There's no gap or overlap between them, and they add up to the full LCP time.\n\n| LCP subpart                   | % of LCP (Optimal) | Description                                                                                                                                                              |\n| ----------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| **Time to First Byte (TTFB)** | ~40%               | The time from when the user initiates loading the page until the browser receives the first byte of the HTML document response.                                          |\n| **Resource load delay**       | <10%               | The time between TTFB and when the browser starts loading the LCP resource. If the LCP element doesn't require a resource load (e.g., system font text), this time is 0. |\n| **Resource load duration**    | ~40%               | The duration of time it takes to load the LCP resource itself. If the LCP element doesn't require a resource load, this time is 0.                                       |\n| **Element render delay**      | <10%               | The time between when the LCP resource finishes loading and the LCP element rendering fully.                                                                             |\n\n## Why the Breakdown Matters\n\nOptimizing for LCP requires identifying which of these subparts is the bottleneck:\n\n- **Large delta between TTFB and FCP**: Indicates the browser needs to download a lot of render-blocking assets or complete a lot of work (e.g., client-side rendering).\n- **Large delta between FCP and LCP**: Indicates the LCP resource is not immediately available for the browser to prioritize or the browser is completing other work before it can display the LCP content.\n- **Large resource load delay**: Indicates the resource is not discoverable early or is deprioritized.\n- **Large element render delay**: Indicates rendering is blocked by stylesheets, scripts, or long tasks.\n","skills/debug-optimize-lcp/references/lcp-snippets.md":"# LCP Debugging Snippets\n\nUse these JavaScript snippets with the `evaluate_script` tool to extract deep insights from the page.\n\n## 1. Identify LCP Element\n\nUse this snippet to identify the LCP element and get raw timing data from the Performance API.\n\n```javascript\nasync () => {\n  return await new Promise(resolve => {\n    new PerformanceObserver(list => {\n      const entries = list.getEntries();\n      const last = entries[entries.length - 1];\n      resolve({\n        element: last.element?.tagName,\n        id: last.element?.id,\n        className: last.element?.className,\n        url: last.url,\n        startTime: last.startTime,\n        renderTime: last.renderTime,\n        loadTime: last.loadTime,\n        size: last.size,\n      });\n    }).observe({type: 'largest-contentful-paint', buffered: true});\n  });\n};\n```\n\n## 2. Audit Common Issues\n\nUse this snippet to check for common DOM-based LCP issues (lazy loading, priority).\n\n```javascript\n() => {\n  const issues = [];\n\n  // Check for lazy-loaded images in viewport\n  document.querySelectorAll('img[loading=\"lazy\"]').forEach(img => {\n    const rect = img.getBoundingClientRect();\n    if (rect.top < window.innerHeight) {\n      issues.push({\n        issue: 'lazy-loaded image in viewport',\n        element: img.outerHTML.substring(0, 200),\n        fix: 'Remove loading=\"lazy\" from this image — it is in the initial viewport and may be the LCP element',\n      });\n    }\n  });\n\n  // Check for LCP-candidate images missing fetchpriority\n  document.querySelectorAll('img:not([fetchpriority])').forEach(img => {\n    const rect = img.getBoundingClientRect();\n    if (rect.top < window.innerHeight && rect.width * rect.height > 50000) {\n      issues.push({\n        issue: 'large viewport image without fetchpriority',\n        element: img.outerHTML.substring(0, 200),\n        fix: 'Add fetchpriority=\"high\" to this image — it is large and visible in the initial viewport',\n      });\n    }\n  });\n\n  // Check for render-blocking scripts in head\n  document\n    .querySelectorAll(\n      'head script:not([async]):not([defer]):not([type=\"module\"])',\n    )\n    .forEach(script => {\n      if (script.src) {\n        issues.push({\n          issue: 'render-blocking script in head',\n          element: script.outerHTML.substring(0, 200),\n          fix: 'Add async or defer attribute, or move to end of body',\n        });\n      }\n    });\n\n  return {issueCount: issues.length, issues};\n};\n```\n","skills/debug-optimize-lcp/references/optimization-strategies.md":"# LCP Optimization Strategies\n\n## 1. Eliminate Resource Load Delay\n\n**Goal**: Ensure the LCP resource starts loading as early as possible.\n\n- **Early Discovery**: Ensure the LCP resource is discoverable in the initial HTML document response (not dynamically added by JS or hidden in `data-src`).\n- **Preload**: Use `<link rel=\"preload\">` with `fetchpriority=\"high\"` for critical images or fonts.\n- **Avoid Lazy Loading**: Never set `loading=\"lazy\"` on the LCP image.\n- **Fetch Priority**: Use `fetchpriority=\"high\"` on the `<img>` tag.\n- **Same Origin**: Host critical resources on the same origin or use `<link rel=\"preconnect\">`.\n\n## 2. Eliminate Element Render Delay\n\n**Goal**: Ensure the LCP element can render immediately after its resource has finished loading.\n\n- **Minimize Render-Blocking CSS**: Inline critical CSS and defer non-critical CSS. Ensure the stylesheet is smaller than the LCP resource.\n- **Minimize Render-Blocking JS**: Avoid synchronous scripts in the `<head>`. Inline very small scripts.\n- **Server-Side Rendering (SSR)**: Deliver the full HTML markup from the server so image resources are discoverable immediately.\n- **Break Up Long Tasks**: Prevent large JavaScript tasks from blocking the main thread during rendering.\n\n## 3. Reduce Resource Load Duration\n\n**Goal**: Reduce the time spent transferring the bytes of the resource.\n\n- **Optimize Resource Size**: Serve optimal image sizes, use modern formats (AVIF, WebP), and compress images/fonts.\n- **Geographic Proximity (CDN)**: Use a Content Delivery Network to get servers closer to users.\n- **Reduce Contention**: Use `fetchpriority=\"high\"` to prevent lower-priority resources from competing for bandwidth.\n- **Caching**: Use efficient `Cache-Control` policies.\n\n## 4. Reduce Time to First Byte (TTFB)\n\n**Goal**: Deliver the initial HTML as quickly as possible.\n\n- **Minimize Redirects**: Avoid multiple redirects from advertisements or shortened links.\n- **CDN Caching**: Cache static HTML documents at the edge.\n- **Edge Computing**: Move dynamic logic to the edge to avoid trips to the origin server.\n- **Back/Forward Cache**: Ensure pages are eligible for bfcache.\n","skills/memory-leak-debugging/SKILL.md":"---\nname: memory-leak-debugging\ndescription: Diagnoses and resolves memory leaks in JavaScript/Node.js applications. Use when a user reports high memory usage, OOM errors, or wants to capture, compare, or inspect heap snapshots with Chrome DevTools MCP memory tools.\n---\n\n# Memory Leak Debugging\n\nThis skill provides expert guidance and workflows for finding, diagnosing, and fixing memory leaks in JavaScript and Node.js applications using Chrome DevTools MCP tools.\n\n## Prerequisites\n\nAdvanced memory debugging tools (`compare_heapsnapshots`, `get_heapsnapshot_details`, etc.) are only available when the server is started with the `--memoryDebugging` flag. First check if these tools are available; if not, try to read the MCP configuration file to check if `--memoryDebugging` is enabled.\n\n## Core Principles\n\n- **Prefer MCP memory tools:** Do NOT attempt to read raw `.heapsnapshot` files directly, as they are extremely large and will consume too many tokens. Use the Chrome DevTools MCP heap snapshot tools to summarize, compare, and inspect snapshots.\n- **Isolate the Leak:** Determine if the leak is in the browser (client-side) or Node.js (server-side).\n- **Common Culprits:** Look for detached DOM nodes, unhandled closures, global variables, event listeners not being removed, and caches growing unbounded. _Note: Detached DOM nodes are sometimes intentional caches; always ask the user before nulling them._\n- **Close Loaded Snapshots:** Heap snapshots can be large. After completing an investigation, use `close_heapsnapshot` for each loaded snapshot to release memory held by the MCP server.\n\n## Workflows\n\n### 1. Capturing Snapshots\n\nWhen investigating a frontend web application memory leak, utilize the `chrome-devtools-mcp` tools to interact with the application and take snapshots.\n\n- Use page-scoped tools like `click`, `navigate_page`, `fill`, etc. (specifying `pageId`) to manipulate the page into the desired state.\n- Revert the page back to the original state after interactions to see if memory is released.\n- Repeat the same user interactions 10 times to amplify the leak.\n- Use `take_heapsnapshot` (with `pageId`) to save `.heapsnapshot` files to disk at baseline, target (after actions), and final (after reverting actions) states.\n\n### 2. Comparing Snapshots\n\nOnce you have generated `.heapsnapshot` files using `take_heapsnapshot`, compare them with Chrome DevTools MCP memory tools.\n\n- Start with `get_heapsnapshot_summary` for each snapshot to confirm that the files load and to compare high-level totals.\n- Use `compare_heapsnapshots` to compare baseline and target snapshots. Start without `classIndex` for the summary diff, then request detailed class diffs only for suspicious growth by specifying `classIndex`.\n- Use the summary output from `compare_heapsnapshots` before drilling into specific node IDs.\n\n### 3. Inspecting Retainers and Dominator Chains\n\nWhen a class or object type grows unexpectedly, inspect the retaining chain and dominators with the MCP tools before changing code.\n\n- Use `get_heapsnapshot_class_nodes` to list instances of the suspicious class.\n- Use `get_heapsnapshot_retainers`, `get_heapsnapshot_retaining_paths`, `get_heapsnapshot_dominators`, and `get_heapsnapshot_edges` to understand why representative nodes are still reachable.\n- Use `get_heapsnapshot_object_details` with a specific `nodeId` to retrieve detailed object metadata (size, type, distance, and DOM detachedness).\n- Use `get_heapsnapshot_duplicate_strings` when string growth dominates the diff.\n- Read [references/common-leaks.md](references/common-leaks.md) for examples of common memory leaks and how to fix them after the retaining path points at application code.\n\n### 4. Advanced Analysis and Categorized Filters\n\nUse built-in MCP memory tools and filters to pinpoint specific leak categories directly without external tools.\n\n- Use `get_heapsnapshot_details` or `get_heapsnapshot_class_nodes` with `filterName` to target common leak causes:\n  - `objectsRetainedByDetachedDomNodes`: Identifies detached DOM elements retained in memory.\n  - `objectsRetainedByEventHandlers`: Identifies objects kept alive by unremoved event listeners.\n  - `objectsRetainedByContexts`: Identifies objects trapped in closures or execution contexts.\n  - `objectsRetainedByConsole`: Identifies objects retained by console logging.\n","skills/memory-leak-debugging/references/common-leaks.md":"# Common Memory Leaks\n\nWhen analyzing retaining paths, dominator chains, or class diffs with Chrome DevTools MCP memory tools, look for these common patterns in the codebase:\n\n## 1. Uncleared Event Listeners\n\nEvent listeners attached to global objects (like `window` or `document`) or long-living objects prevent garbage collection of the objects referenced in their callbacks.\n\n**Fix:** Always call `removeEventListener` when a component unmounts or the listener is no longer needed.\n\n## 2. Detached DOM Nodes\n\nA DOM node is removed from the document tree but is still referenced by a JavaScript variable. While detachedness is a good signal for a memory leak, it's not always a bug. For example, websites sometimes intentionally cache detached navigation trees.\n\n**Fix:** Signal the detached nodes to the user first. **Ask the user first** before nulling the references or changing the code, as the detached nodes might be part of an intentional cache. If confirmed as a leak, ensure variables holding DOM references are set to `null` when the node is removed, or limit their scope.\n\n## 3. Unintentional Global Variables\n\nVariables declared without `var`, `let`, or `const` (in non-strict mode) or explicitly attached to `window` remain in memory forever.\n\n**Fix:** Use strict mode, properly declare variables, and avoid global state.\n\n## 4. Closures\n\nClosures can unintentionally keep references to large objects in their outer scope.\n\n**Fix:** Nullify large objects when they are no longer needed, or refactor the closure to not capture unnecessary variables.\n\n## 5. Unbounded Caches or Arrays\n\nData structures used for caching (like objects, Arrays, or Maps) that grow without limits.\n\n**Fix:** Implement caching limits, use LRU caches, or use `WeakMap`/`WeakSet` for data associated with object lifecycles.\n","skills/troubleshooting/SKILL.md":"---\nname: troubleshooting\ndescription: Uses Chrome DevTools MCP and documentation to troubleshoot connection and target issues. Trigger this skill when list_pages, new_page, or navigate_page fail, or when the server initialization fails.\n---\n\n## Troubleshooting Wizard\n\nYou are acting as a troubleshooting wizard to help the user configure and fix their Chrome DevTools MCP server setup. When this skill is triggered (e.g., because `list_pages`, `new_page`, or `navigate_page` failed, or the server wouldn't start), follow this step-by-step diagnostic process:\n\n### Step 1: Find and Read Configuration\n\nYour first action should be to locate and read the MCP configuration file. Search for the following files in the user's workspace: `.mcp.json`, `.vscode/mcp.json`, `gemini-extension.json`, or `.gemini/settings.json` (or user-scoped files such as `~/.claude.json` if accessible).\n\nIf you find a configuration file, read and interpret it to identify potential issues such as:\n\n- Incorrect arguments or flags.\n- Missing environment variables.\n- Usage of `--autoConnect` in incompatible environments.\n\nIf you cannot find any of these files, only then should you ask the user to provide their configuration file content.\n\n### Step 2: Triage Common Connection Errors\n\nBefore reading documentation or suggesting configuration changes, check if the error message matches one of the following common patterns.\n\n#### Error: `Could not find DevToolsActivePort`\n\nThis error is highly specific to the `--autoConnect` feature. It means the MCP server cannot find the file created by a running, debuggable Chrome instance. This is not a generic connection failure.\n\nYour primary goal is to guide the user to ensure Chrome is running and properly configured. Do not immediately suggest switching to `--browserUrl`. Follow this exact sequence:\n\n1. **Ask the user to confirm that the correct Chrome version** (e.g., \"Chrome Canary\" if the error mentions it) is currently running.\n2. **If the user confirms it is running, instruct them to enable remote debugging.** Be very specific about the URL and the action: \"Please open a new tab in Chrome, navigate to `chrome://inspect/#remote-debugging`, and make sure the 'Enable remote debugging' checkbox is checked.\"\n3. **Once the user confirms both steps, your only next action should be to call the `list_pages` tool.** This is the simplest and safest way to verify if the connection is now successful. Do not retry the original, more complex command yet.\n4. **If `list_pages` succeeds, the problem is resolved.** If it still fails with the same error, then you can proceed to the more advanced steps like suggesting `--browserUrl` or checking for sandboxing issues.\n\n#### Symptom: Server starts but creates a new empty profile\n\nIf the server starts successfully but `list_pages` returns an empty list or creates a new profile instead of connecting to the existing Chrome instance, check for typos in the arguments.\n\n- **Check for flag typos:** For example, `--autoBronnect` instead of `--autoConnect`.\n- **Verify the configuration:** Ensure the arguments match the expected flags exactly.\n\n#### Symptom: Missing Tools / Only a few tools available\n\nIf the server starts successfully but only a limited subset of tools (like `list_pages`, `list_console_messages`, `get_console_message`, `list_network_requests`) are available, this is likely because the MCP client is enforcing a **read-only mode**.\n\nAll tools in `chrome-devtools-mcp` are annotated with `readOnlyHint: true` (for safe, non-modifying tools) or `readOnlyHint: false` (for tools that modify browser state, like `emulate`, `click`, `navigate_page`). To access the full suite of tools, the user must disable read-only mode in their MCP client (e.g., by exiting \"Plan Mode\" in Gemini CLI or adjusting their client's tool safety settings).\n\n#### Symptom: Extension tools are missing or extensions fail to load\n\nIf the tools related to extensions (like `install_extension`) are not available, or if the extensions you load are not functioning:\n\n1. **Check for the `--categoryExtensions` flag**: Ensure this flag is passed in the MCP server configuration to enable the extension category tools.\n\n#### Other Common Errors\n\nIdentify other error messages from the failed tool call or the MCP initialization logs:\n\n- `Target closed`\n- \"Tool not found\" (check if they are using `--slim` which only enables navigation, script evaluation and screenshot tools).\n- Missing `pageId`: Page-scoped tools require a `pageId` argument. Call `list_pages` to find active page IDs.\n- `ProtocolError: Network.enable timed out` or `The socket connection was closed unexpectedly`\n- `Error [ERR_MODULE_NOT_FOUND]: Cannot find module`\n- Any sandboxing or host validation errors.\n\n### Step 3: Read Known Issues\n\nRead the contents of https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/troubleshooting.md to map the error to a known issue. Pay close attention to:\n\n- Sandboxing restrictions (macOS Seatbelt, Linux containers).\n- WSL requirements.\n- `--autoConnect` handshakes, timeouts, and requirements (requires **running** Chrome 144+).\n\n### Step 4: Formulate a Configuration\n\nBased on the exact error and the user's environment (OS, MCP client), formulate the correct MCP configuration snippet. Check if they need to:\n\n- Pass `--browser-url=http://127.0.0.1:9222` instead of `--autoConnect` (e.g. if they are in a sandboxed environment like Claude Desktop).\n- Enable remote debugging in Chrome (`chrome://inspect/#remote-debugging`) and accept the connection prompt. **Ask the user to verify this is enabled if using `--autoConnect`.**\n- Add `--logFile <absolute_path_to_log_file>` to capture debug logs for analysis.\n- Increase `startup_timeout_ms` (e.g. to 20000) if using Codex on Windows.\n\n_If you are unsure of the user's configuration, ask the user to provide their current MCP server JSON configuration._\n\n### Step 5: Run Diagnostic Commands\n\nIf the issue is still unclear, run diagnostic commands to test the server directly:\n\n- Run `npx chrome-devtools-mcp@latest --help` to verify the installation and Node.js environment.\n- If you need more information, run `NODE_DEBUG=* npx chrome-devtools-mcp@latest --logFile=/tmp/cdm-test.log` to capture verbose logs. Analyze the output for errors.\n\n### Step 6: Check GitHub for Existing Issues\n\nIf https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/troubleshooting.md does not cover the specific error, check if the `gh` (GitHub CLI) tool is available in the environment. If so, search the GitHub repository for similar issues:\n`gh issue list --repo ChromeDevTools/chrome-devtools-mcp --search \"<error snippet>\" --state all`\n\nAlternatively, you can recommend that the user checks https://github.com/ChromeDevTools/chrome-devtools-mcp/issues and https://github.com/ChromeDevTools/chrome-devtools-mcp/discussions for help.\n"},"files":{".claude-plugin/marketplace.json":"{\n  \"name\": \"chrome-devtools-plugins\",\n  \"version\": \"1.12.0\",\n  \"description\": \"Bundled plugins for actuating and debugging the Chrome browser.\",\n  \"repository\": \"https://github.com/ChromeDevTools/chrome-devtools-mcp\",\n  \"owner\": {\n    \"name\": \"Chrome DevTools Team\",\n    \"email\": \"devtools-dev@chromium.org\"\n  },\n  \"plugins\": [\n    {\n      \"name\": \"chrome-devtools-mcp\",\n      \"source\": \"./\",\n      \"description\": \"Reliable automation, in-depth debugging, and performance analysis in Chrome using Chrome DevTools and Puppeteer\"\n    }\n  ]\n}\n",".claude-plugin/plugin.json":"{\n  \"name\": \"chrome-devtools-mcp\",\n  \"version\": \"1.12.0\",\n  \"description\": \"Reliable automation, in-depth debugging, and performance analysis in Chrome using Chrome DevTools and Puppeteer\",\n  \"mcpServers\": {\n    \"chrome-devtools\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"chrome-devtools-mcp@1.12.0\"\n      ]\n    }\n  }\n}\n",".cursor-plugin/plugin.json":"{\n  \"name\": \"devtools-for-agents\",\n  \"description\": \"Help your agent build, debug, and verify your code correctly. With Chrome DevTools for agents, your AI agent can interact with the Chrome browser to test code, emulate users, and catch bugs using Chrome DevTools’ capabilities before shipping.\",\n  \"version\": \"1.12.0\",\n  \"author\": {\n    \"name\": \"Google Chrome\"\n  },\n  \"logo\": \"https://github.com/ChromeDevTools/devtools-logo/blob/master/logos/svg/chrome-devtools-square-responsive.svg\",\n  \"homepage\": \"https://developer.chrome.com/docs/devtools/agents\",\n  \"repository\": \"https://github.com/ChromeDevTools/chrome-devtools-mcp\",\n  \"mcpServers\": {\n    \"chrome-devtools\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"chrome-devtools-mcp@1.12.0\"\n      ]\n    }\n  }\n}\n",".github/plugin/plugin.json":"{\n  \"name\": \"chrome-devtools-mcp\",\n  \"version\": \"1.12.0\",\n  \"description\": \"Reliable automation, in-depth debugging, and performance analysis in Chrome using Chrome DevTools and Puppeteer\",\n  \"mcpServers\": {\n    \"chrome-devtools\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"chrome-devtools-mcp@1.12.0\"\n      ]\n    }\n  }\n}\n",".kimi-code-plugin/plugin.json":"{\n  \"name\": \"chrome-devtools\",\n  \"description\": \"Help your agent build, debug, and verify your code correctly. With Chrome DevTools for agents, your AI agent can interact with the Chrome browser to test code, emulate users, and catch bugs using Chrome DevTools’ capabilities before shipping.\",\n  \"version\": \"1.12.0\",\n  \"author\": {\n    \"name\": \"Google Chrome\"\n  },\n  \"logo\": \"https://github.com/ChromeDevTools/devtools-logo/blob/master/logos/svg/chrome-devtools-square-responsive.svg\",\n  \"homepage\": \"https://developer.chrome.com/docs/devtools/agents\",\n  \"repository\": \"https://github.com/ChromeDevTools/chrome-devtools-mcp\",\n  \"mcpServers\": {\n    \"chrome-devtools\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"chrome-devtools-mcp@1.12.0\"\n      ]\n    }\n  }\n}\n","AGENTS.md":"This repository contains an MCP server and CLI for Chrome DevTools.\n\n# Instructions\n\n- Use only scripts from `package.json` to run commands.\n- Use `npm run build` to run tsc and test build.\n- Use `npm run test` to build and run tests, run all tests to verify correctness.\n- Use `npm run test path-to-test.ts` to build and run a single test file, for example, `npm run test tests/McpContext.test.ts`.\n- Use `npm run format` to fix formatting and get linting errors.\n- Never modify `third_party/devtools-frontend` except for experimentation: it is a git submodule, a mirror of the actual codebase.\n\n## Rules for TypeScript\n\n- Do not use `any` type.\n- Do not use `as` keyword for type casting.\n- Do not use `!` operator for type assertion.\n- Do not use `// @ts-ignore` comments.\n- Do not use `// @ts-nocheck` comments.\n- Do not use `// @ts-expect-error` comments.\n- Prefer `for..of` instead of `forEach`.\n- Never type-check types that are already type safe (e.g. redundant `typeof` checks on statically typed variables).\n\n## Rules for Testing\n\n### Structure and Separation of Concerns\n\n- **Prefer mock-based unit tests over real-browser tests**: Do not use `withMcpContext` or launch a real browser unless the test genuinely requires real browser or DevTools protocol integration (e.g., live CDP events, browser lifecycle, secondary sessions). Puppeteer already tests browser behavior upstream; unit tests run faster and avoid browser overhead.\n- **Tool handler tests (`tests/tools/*.test.ts`)**:\n  - Test that the tool handler parses/validates parameters and invokes the corresponding methods on `page`, `context`, or `response` with the exact expected arguments.\n  - Do **not** reimplement business logic or state tracking inside mocks (e.g., do not simulate state changes in mock methods).\n- **Core class tests (e.g., `tests/McpPage.test.ts`)**:\n  - Test business logic by instantiating the real class under test with mocked dependencies (e.g., instantiate `new McpPage(...)` with a mocked Puppeteer page from `createMockPuppeteerPage()`).\n  - Assert that the class calls the underlying Puppeteer methods with the expected parameters.\n\n### Mocking Guidelines (`tests/mocks.ts`)\n\n- **Centralize mocks in `tests/mocks.ts`**: Keep all reusable mock factories in `tests/mocks.ts`. Import directly from `tests/mocks.ts` (do not re-export from `tests/utils.ts`).\n- **Use `sinon.createStubInstance(Class)`**: Do not hand-roll mock objects or define custom mock interfaces. Use `sinon.createStubInstance()` so all prototype methods are automatically stubbed.\n- **Typing**: Use `sinon.SinonStubbedInstance<Class>` for mock types (e.g., `MockMcpPage`, `MockMcpContext`, `MockMcpResponse`).\n- **Handler mocks helper**: For tool handlers, use `const {page, context, response} = createHandlerMocks();` from `tests/mocks.ts` to set up all three mocks in one call.\n- **Keep mocks generic**: Do not tailor mocks to a specific tool or test suite.\n- **Naming conventions**: Use `mock` rather than `fake` in helper and variable names (e.g., `createMockPuppeteerPage`, `createMockMcpPage`). Name the mocked Puppeteer page instance `pptrPage`.\n\n### Assertions and Sinon Best Practices\n\n- **Use `sinon.assert` methods**: Do not use Node's `assert.ok(stub.calledOnce)` or `assert.deepStrictEqual(stub.firstCall.args[0], ...)` to verify stub calls.\n- **Verify exact arguments**:\n  - Use `sinon.assert.calledOnceWithExactly(stub, ...args)` for single calls with exact arguments.\n  - Use `sinon.assert.calledWithExactly(stub.secondCall, ...args)` for subsequent calls.\n  - Use `sinon.assert.notCalled(stub)` to assert a method was not invoked.\n- **Clean up stubs**: `tests/setup.ts` runs `sinon.restore()` after every test. Do not add `afterEach(() => sinon.restore());` to test suites.\n\n### Test Cleanliness\n\n- Do not add redundant comments or verbose JSDoc for short, self-describing mock functions or tests.\n- Only test real scenarios; avoid testing redundant or artificial calls that cannot happen in real usage.\n- Use current year (2026) in copyright headers for new test files.\n\n# Pre-submit checklist\n\nComplete every item before you upload a PR or push a new revision.\n\n## Scope and PR description\n\n- [ ] The PR contains one change. Move unrelated refactors, renames,\n      formatting changes, and build or compiler changes into their own PR.\n- [ ] The PR title, the commit type (`fix:`, `feat:`, `chore:`), and the\n      squash commit message describe the same change. A change to a default\n      value or to tool output is a `fix:` or a `feat:`, not a `chore:`.\n- [ ] The PR description lists every user-visible behavior change, including\n      changes to other tools that share the changed code path.\n- [ ] The PR description matches the shipped behavior. Remove claims about\n      fixes that the diff does not contain.\n- [ ] The PR description names new functions that have no production caller\n      yet, or the PR removes them.\n- [ ] The branch has no merge conflicts with `main`. Rebase only when a\n      conflict exists.\n- [ ] `npm run gen` ran after a change to a tool schema, a tool description,\n      or a CLI option. The generated files are committed.\n- [ ] `npm run format` ran. No line is longer than 80 columns.\n- [ ] `npm run test` passed locally. CI failures on all platforms are your\n      responsibility.\n\n## Design\n\n- [ ] The change lives in the lowest layer that owns the data: page logic in\n      `McpPage`, context logic in `McpContext`, formatting in\n      `src/formatters/*`. `McpResponse` does not resolve pages or workers.\n- [ ] If Puppeteer or devtools-frontend owns the bug, fix it there first. Do\n      not copy or re-implement upstream code in this repository.\n- [ ] Puppeteer internals are imported only through `src/third_party/index.ts`.\n- [ ] Repeated code is one helper. Extract a helper when the same block\n      appears two or more times. Prefer an existing helper or a Puppeteer\n      utility (for example `Deferred`) over a new one.\n- [ ] A new formatter class exposes `toJSON()` and `toString()`, and\n      `toString()` uses the `toJSON()` output.\n- [ ] A function with three or more optional parameters takes one options\n      object. Do not pass positional `undefined` placeholders.\n- [ ] Each default value is declared in one place.\n- [ ] A new behavior is opt-in by default when a wrong classification fails\n      silently (for example a config option is restart-required unless the\n      author marks it reloadable).\n- [ ] An invalid combination of flags or options is rejected by the type\n      system where possible, not only by a comment.\n- [ ] No public method exists only to serve a test. Put shared helpers in\n      `src/utils/*`.\n- [ ] Parameter validation lives in the Zod schema (`refine`, `coerce`), not\n      in the handler. Validation runs before the handler changes any state.\n- [ ] Numeric validation matches what the consumer accepts. Use\n      `Number.isInteger` and a range check where CDP expects integers.\n\n## Correctness and edge cases\n\n- [ ] A failed external call does not leave stale state. Restore the previous\n      value when a call rejects.\n- [ ] An error path still produces the artifacts and partial results the\n      agent needs before it throws.\n- [ ] An async operation that a timeout can abandon checks that its target\n      (browser, page, config) is still current when it resumes.\n- [ ] A lock or guard is acquired and released exactly once. A helper that\n      releases a guard throws when the guard is already released.\n- [ ] Concurrent callbacks (watchers, reloads, event handlers) are\n      serialized. A transient failure does not trigger a callback.\n- [ ] A filesystem operation that races with another process tolerates the\n      race (`fs.rmSync(path, {force: true})` or `try/catch`), not\n      `existsSync` + an unguarded call.\n- [ ] A failed lookup falls back to the previous behavior. It does not turn\n      a healthy state into an error state.\n- [ ] No synchronous process spawn or blocking call runs in a polling loop\n      or in a hot function.\n- [ ] `dispose()` releases every resource the object created, and runs for\n      objects that are already closed.\n- [ ] Event listeners added for one action are removed when the action ends.\n- [ ] Cached page-side state is refreshed before use. The page can reload\n      between tool calls.\n- [ ] Behavior shared by all tools that act on the selected page also runs\n      for tools defined with `defineTool`, not only `definePageTool`.\n- [ ] String and URL parsing handles userinfo, escaped delimiters, ports,\n      pattern groups, and empty or whitespace-only input. Use the full\n      authority, not a heuristic on the first or last character.\n- [ ] Truncation uses `Intl.Segmenter` graphemes, not `string.slice`.\n- [ ] Secrets never reach logs. Redact headers, URL userinfo, and query\n      strings in every log line, including process startup arguments.\n- [ ] A filesystem path parameter is validated with `validatePath()` even\n      when the value parses as a URL. The error says what the parameter\n      expects.\n- [ ] A value that crosses a process boundary (daemon IPC, CLI) is\n      serialized with `JSON.stringify` when it is not a primitive.\n- [ ] An identifier is issued only for items that a tool can resolve later.\n- [ ] A dedup key never collapses for items without identifying fields.\n      Use a counter or skip such items.\n- [ ] An error message names the exact condition that failed, with the\n      relevant values (pid, timeout, URL, parameter name).\n- [ ] A thrown non-`Error` value keeps its content in the error message.\n      Treat objects with a string `message` like `Error`.\n- [ ] Optional chaining and `?? default` are not applied to values whose\n      type is non-optional.\n- [ ] Version-dependent behavior (Node, Chrome, OS) is tested on every\n      version the `engines` field allows, or the test does not depend on it.\n\n## Tests\n\n- [ ] Every new or changed function has a test that calls it. A function\n      with no caller in `src/` and no test is removed from the PR.\n- [ ] Every review request of the form \"add a test for X\" has a test whose\n      name includes X.\n- [ ] A new test for a bug fails on `main` without the fix. Remove\n      assertions that pass with and without the fix.\n- [ ] A `notCalled` assertion targets the first method the handler calls\n      after the step under test, not a method inside a callback that a stub\n      never runs.\n- [ ] A test for a selected branch also asserts that the other branch was\n      not taken.\n- [ ] Assertions use `calledOnceWithExactly` or\n      `calledWithExactly(stub.nthCall, ...)`. Use `calledOnceWithMatch` only\n      for arguments that cannot be exact (for example an `AbortSignal`) or\n      when an overloaded signature forces `sinon.match.same`.\n- [ ] Options that the handler derives from its input are asserted, not\n      only the call count.\n- [ ] Test names describe the asserted behavior. Rename the test when the\n      assertion changes.\n- [ ] `if (value) { assert... }` does not appear in tests. Use\n      `assert.ok(value)` before the dependent assertions.\n- [ ] Real-browser tests exist only for behavior that mocks cannot observe.\n      A test file contains at most one slow end-to-end run per scenario.\n- [ ] A real-browser test that counts requests filters `favicon.ico`.\n- [ ] A real-browser test that waits for CDP events waits on the session or\n      result of the code under test, not on a new `createCDPSession()`.\n- [ ] Every awaited event in a test has a bounded timeout. Positive-path\n      tests poll for the condition instead of a fixed sleep.\n- [ ] New reusable stubs and fixtures live in `tests/mocks.ts` and build on\n      `sinon.createStubInstance`. No hand-rolled fixture uses `as unknown as`.\n- [ ] Mock options that equal the default value are not passed.\n- [ ] Temporary directories use `using dir = createTempDir(...)` from\n      `tests/utils.ts`, not `fs.mkdtemp`.\n- [ ] A test never reads the developer's home directory and never calls\n      `process.exit`. Stub locators and pass `exitProcess = false`.\n- [ ] When a real-browser test is replaced with mocks, each branch the old\n      test covered still has a test.\n- [ ] A test does not repeat a round-trip that another test already covers.\n      Stop after the first assertion that proves the change.\n\n## Documentation and output\n\n- [ ] A tool description states the exact behavior, including when a\n      parameter is ignored or depends on another parameter.\n- [ ] Skills stay generic. Add one short note in the tool-selection section\n      instead of a feature-specific workflow.\n- [ ] Inline comments are one sentence. Remove comments that repeat an\n      existing TSDoc.\n- [ ] Response text that depends on a condition prints only when the\n      condition is true.\n","plugin.json":"{\n  \"$schema\": \"https://agent-plugins.org/schemas/1.0.0/plugin.schema.json\",\n  \"name\": \"chrome-devtools\",\n  \"version\": \"1.12.0\",\n  \"description\": \"Reliable Chrome automation, debugging, and performance analysis for AI agents.\",\n  \"author\": {\n    \"name\": \"Google Chrome\",\n    \"url\": \"https://developer.chrome.com/\"\n  },\n  \"homepage\": \"https://developer.chrome.com/docs/devtools/agents\",\n  \"repository\": \"https://github.com/ChromeDevTools/chrome-devtools-mcp\",\n  \"license\": \"Apache-2.0\",\n  \"keywords\": [\n    \"browser\",\n    \"chrome\",\n    \"debugging\",\n    \"mcp\",\n    \"performance\"\n  ]\n}\n","skills/a11y-debugging/SKILL.md":"---\nname: a11y-debugging\ndescription: Uses Chrome DevTools MCP for accessibility (a11y) debugging and auditing based on web.dev guidelines. Use when testing semantic HTML, ARIA labels, focus states, keyboard navigation, tap targets, and color contrast.\n---\n\n## Core Concepts\n\n**Page Targeting**: Page-scoped tools (`take_snapshot`, `list_console_messages`, `evaluate_script`, `press_key`, `take_screenshot`, `lighthouse_audit`, etc.) require a `pageId` parameter. Retrieve available page IDs using `list_pages` or from `new_page`.\n\n**Accessibility Tree vs DOM**: Visually hiding an element (e.g., `CSS opacity: 0`) behaves differently for screen readers than `display: none` or `aria-hidden=\"true\"`. The `take_snapshot` tool returns the accessibility tree of the page, which represents what assistive technologies \"see\", making it the most reliable source of truth for semantic structure.\n\n**Reading web.dev documentation**: If you need to research specific accessibility guidelines (like `https://web.dev/articles/accessible-tap-targets`), you can append `.md.txt` to the URL (e.g., `https://web.dev/articles/accessible-tap-targets.md.txt`) to fetch the clean, raw markdown version. This is much easier to read!\n\n## Workflow Patterns\n\n### 1. Automated Audit (Lighthouse)\n\nStart by running a Lighthouse accessibility audit to get a comprehensive baseline. This tool provides a high-level score and lists specific failing elements with remediation advice.\n\n1.  Run the audit:\n    - Set `mode` to `\"navigation\"` to refresh the page and capture load issues.\n    - Omit `outputDirPath` to save reports to the default temporary directory (the exact path to `report.json` is returned under `Reports`), or set `outputDirPath` to a directory within the workspace.\n2.  **Analyze the Summary**:\n    - In the default text output, category scores use a 0-100 scale (100 is a perfect score), followed by `Passed: N` and `Failed: N` audit counts.\n    - If the server was started with `--experimentalStructuredContent`, inspect `summary.scores[].score` (0-1 scale) and `summary.audits.failed` in the structured content instead.\n3.  **Review the Report (CRITICAL)**:\n    - **Parsing**: Do not read the entire file line-by-line. Use a CLI tool like `jq` or a Node.js one-liner to filter for failures from the report path returned by `lighthouse_audit`:\n      ```bash\n      # Extract failing audits with their details (replace with report.json path from tool response)\n      node -e \"const r=require('<path/to/report.json>'); Object.values(r.audits).filter(a=>a.score!==null && a.score<1).forEach(a=>console.log(JSON.stringify({id:a.id, title:a.title, items:a.details?.items})))\"\n      ```\n    - This efficiently extracts the `selector` and `snippet` of failing elements without loading the full report into context.\n\n### 2. Browser Issues & Audits\n\nChrome automatically checks for common accessibility problems. Use `list_console_messages` to check for these native audits:\n\n- `types`: `[\"issue\"]`\n- `includePreservedMessages`: `true` (to catch issues that occurred during page load)\n\nThis often reveals missing labels, invalid ARIA attributes, and other critical errors without manual investigation.\n\n### 3. Semantics & Structure\n\nThe accessibility tree exposes the heading hierarchy and semantic landmarks.\n\n1.  Navigate to the page.\n2.  Use `take_snapshot` to capture the accessibility tree.\n3.  **Check Heading Levels**: Ensure heading levels (`h1`, `h2`, `h3`, etc.) are logical and do not skip levels. The snapshot will include heading roles.\n4.  **Content Reordering**: Verify that the DOM order (which drives the accessibility tree) matches the visual reading order. Use `take_screenshot` to inspect the visual layout and compare it against the snapshot structure to catch CSS floats or absolute positioning that jumbles the logical flow.\n\n### 4. Labels, Forms & Text Alternatives\n\n1.  Locate buttons, inputs, and images in the `take_snapshot` output.\n2.  Ensure interactive elements have an accessible name (e.g., a button should not just say `\"\"` if it only contains an icon).\n3.  **Orphaned Inputs**: Verify that all form inputs have associated labels. Use `evaluate_script` with the **\"Find Orphaned Form Inputs\" snippet** found in [references/a11y-snippets.md](references/a11y-snippets.md).\n4.  Check images for `alt` text.\n\n### 5. Focus & Keyboard Navigation\n\nTesting \"keyboard traps\" and proper focus management without visual feedback relies on tracking the focused element.\n\n1.  Use the `press_key` tool with `\"Tab\"` or `\"Shift+Tab\"` to move focus.\n2.  Use `take_snapshot` to capture the updated accessibility tree.\n3.  Locate the element marked as focused in the snapshot to verify focus moved to the expected interactive element.\n4.  If a modal opens, focus must move into the modal and \"trap\" within it until closed.\n\n### 6. Tap Targets and Visuals\n\nAccording to web.dev, tap targets should be at least 48x48 pixels with sufficient spacing. Since the accessibility tree doesn't show sizes, use `evaluate_script` with the **\"Measure Tap Target Size\" snippet** found in [references/a11y-snippets.md](references/a11y-snippets.md).\n\n_Pass the element's `uid` from the snapshot as an argument to `evaluate_script`._\n\n### 7. Color Contrast\n\nTo verify color contrast ratios, use the **\"Check Color Contrast\" snippet** found in [references/a11y-snippets.md](references/a11y-snippets.md) with `evaluate_script`.\n\n### 8. Global Page Checks\n\nVerify document-level accessibility settings often missed in component testing using the **\"Global Page Checks\" snippet** found in [references/a11y-snippets.md](references/a11y-snippets.md).\n\n## Troubleshooting\n\nIf standard a11y queries fail or the `evaluate_script` snippets return unexpected results:\n\n- **Visual Inspection**: If automated scripts cannot determine contrast (e.g., text over gradient images or complex backgrounds), use `take_screenshot` to capture the element. While models cannot measure exact contrast ratios from images, they can visually assess legibility and identify obvious issues.\n","skills/a11y-debugging/references/a11y-snippets.md":"# Accessibility Debugging Snippets\n\nUse these JavaScript snippets with the `evaluate_script` tool.\n\n## 1. Find Orphaned Form Inputs\n\nFinds form inputs that lack an associated label (no `label[for]`, `aria-label`, `aria-labelledby`, or wrapping `<label>`).\n\n```js\n() =>\n  Array.from(document.querySelectorAll('input, select, textarea'))\n    .filter(i => {\n      const hasId = i.id && document.querySelector(`label[for=\"${i.id}\"]`);\n      const hasAria =\n        i.getAttribute('aria-label') || i.getAttribute('aria-labelledby');\n      return !hasId && !hasAria && !i.closest('label');\n    })\n    .map(i => ({\n      tag: i.tagName,\n      id: i.id,\n      name: i.name,\n      placeholder: i.placeholder,\n    }));\n```\n\n## 2. Measure Tap Target Size\n\nReturns the bounding box dimensions of an element. Pass the element's `uid` from the snapshot as an argument to `evaluate_script`.\n\n```js\nel => {\n  const rect = el.getBoundingClientRect();\n  return {width: rect.width, height: rect.height};\n};\n```\n\n## 3. Check Color Contrast\n\nApproximates the contrast ratio between an element's text color and background color. Pass the element's `uid` to test against WCAG AA (4.5:1 for normal text, 3:1 for large text).\n\n**Note**: This uses a simplified algorithm and may not account for transparency, gradients, or background images. For production-grade auditing, consider injecting `axe-core`.\n\n```js\nel => {\n  function getRGB(colorStr) {\n    const match = colorStr.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/);\n    return match\n      ? [parseInt(match[1]), parseInt(match[2]), parseInt(match[3])]\n      : [255, 255, 255];\n  }\n  function luminance(r, g, b) {\n    const a = [r, g, b].map(function (v) {\n      v /= 255;\n      return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);\n    });\n    return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;\n  }\n\n  const style = window.getComputedStyle(el);\n  const fg = getRGB(style.color);\n  let bg = getRGB(style.backgroundColor);\n\n  const l1 = luminance(fg[0], fg[1], fg[2]);\n  const l2 = luminance(bg[0], bg[1], bg[2]);\n  const ratio = (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);\n\n  return {\n    color: style.color,\n    bg: style.backgroundColor,\n    contrastRatio: ratio.toFixed(2),\n  };\n};\n```\n\n## 4. Global Page Checks\n\nChecks document-level accessibility settings often missed in component testing.\n\n```js\n() => ({\n  lang:\n    document.documentElement.lang ||\n    'MISSING - Screen readers need this for pronunciation',\n  title: document.title || 'MISSING - Required for context',\n  viewport:\n    document.querySelector('meta[name=\"viewport\"]')?.content ||\n    'MISSING - Check for user-scalable=no (bad practice)',\n  reducedMotion: window.matchMedia('(prefers-reduced-motion: reduce)').matches\n    ? 'Enabled'\n    : 'Disabled',\n});\n```\n","skills/chrome-devtools-cli/SKILL.md":"---\nname: chrome-devtools-cli\ndescription: Use this skill to write shell scripts or run shell commands to automate tasks in the browser or otherwise use Chrome DevTools via CLI.\n---\n\nThe `chrome-devtools-mcp` CLI lets you interact with the browser from your terminal.\n\n## Setup\n\n_Note: If this is your very first time using the CLI, see [references/installation.md](references/installation.md) for setup. Installation is a one-time prerequisite and is **not** part of the regular AI workflow._\n\n## AI Workflow\n\n1. **Execute**: Run tools directly. If you don't know the target page's ID, run `chrome-devtools list_pages` to find it. The background server starts implicitly; **do not** run `start`/`status`/`stop` before each use.\n2. **Inspect**: Use `chrome-devtools take_snapshot <pageId>` to get an element `<uid>`.\n3. **Act**: Use `chrome-devtools click <pageId> <uid>`, `chrome-devtools fill <pageId> <uid> <value>`, etc. State persists across commands.\n\nSnapshot example:\n\n```\nuid=1_0 RootWebArea \"Example Domain\" url=\"https://example.com/\"\n  uid=1_1 heading \"Example Domain\" level=\"1\"\n```\n\n## Permissions & File Access\n\nBy default, the CLI has full filesystem access (`--allowUnrestrictedPaths=true`), allowing file-saving parameters (`--filePath`, `--outputDirPath`) and `upload_file` to access files anywhere on the system. Pass `--allowUnrestrictedPaths=false` if you want to restrict file access to the OS temp directory.\n\n## Command Usage\n\n```sh\nchrome-devtools <tool> [arguments] [flags]\n```\n\n- Required arguments are passed positionally; optional arguments use flags.\n- Use `--help` on any command for usage details.\n- Output defaults to plain Markdown-like text; pass `--output-format=json` for JSON.\n\n## Input Automation (<uid> from snapshot)\n\n```bash\nchrome-devtools take_snapshot 1 # Take a text snapshot of the page to get UIDs for elements\nchrome-devtools click 1 \"id\" # Clicks on the provided element\nchrome-devtools click 1 \"id\" --dblClick true --includeSnapshot true # Double clicks and returns a snapshot\nchrome-devtools drag 1 \"src\" \"dst\" # Drag an element onto another element\nchrome-devtools drag 1 \"src\" \"dst\" --includeSnapshot true # Drag an element and return a snapshot\nchrome-devtools fill 1 \"id\" \"text\" # Type text into an input, textarea, or select an option\nchrome-devtools fill 1 \"id\" \"text\" --includeSnapshot true # Fill an element and return a snapshot\nchrome-devtools handle_dialog 1 accept # Handle a browser dialog (accept/dismiss)\nchrome-devtools handle_dialog 1 dismiss --promptText \"hi\" # Dismiss a dialog with prompt text\nchrome-devtools hover 1 \"id\" # Hover over the provided element\nchrome-devtools hover 1 \"id\" --includeSnapshot true # Hover over an element and return a snapshot\nchrome-devtools press_key 1 \"Enter\" # Press a key or key combination (\"Control+A\", \"Escape\")\nchrome-devtools press_key 1 \"Control+A\" --includeSnapshot true # Press a key and return a snapshot\nchrome-devtools type_text 1 \"hello\" # Type text using keyboard into a focused input\nchrome-devtools type_text 1 \"hello\" --submitKey \"Enter\" # Type text and press a submit key\nchrome-devtools upload_file 1 \"id\" \"file.txt\" # Upload a file through a provided element\nchrome-devtools upload_file 1 \"id\" \"file.txt\" --includeSnapshot true # Upload a file and return a snapshot\n```\n\n## Navigation\n\n```bash\nchrome-devtools close_page 1 # Closes the page by its index\nchrome-devtools list_pages # Get a list of pages open in the browser\nchrome-devtools navigate_page 1 --url \"https://example.com\" # Navigates the currently selected page to a URL\nchrome-devtools navigate_page 1 --type \"reload\" --ignoreCache true # Reload page ignoring cache\nchrome-devtools navigate_page 1 --url \"https://example.com\" --timeout 5000 # Navigate with a timeout\nchrome-devtools navigate_page 1 --type \"reload\" --handleBeforeUnload \"accept\" # Handle before unload dialog\nchrome-devtools navigate_page 1 --type \"back\" --initScript \"foo()\" # Navigate back and run an init script\nchrome-devtools new_page \"https://example.com\" # Creates a new page\nchrome-devtools new_page \"https://example.com\" --background true --timeout 5000 # Create new page in background\nchrome-devtools new_page \"https://example.com\" --isolatedContext \"ctx\" # Create new page with isolated context\nchrome-devtools select_page 1 # Select a page as a context for future tool calls\nchrome-devtools select_page 1 --bringToFront true # Select a page and bring it to front\n```\n\n## Emulation\n\nEach `emulate` call replaces all previously configured emulation settings (except `--extraHttpHeaders`), so pass all desired settings in a single call.\n\n```bash\nchrome-devtools emulate 1 --networkConditions \"Offline\" # Emulate network conditions (resets other settings)\nchrome-devtools emulate 1 --cpuThrottlingRate 4 --geolocation \"0,0\" # Emulate CPU throttling and geolocation (resets other settings)\nchrome-devtools emulate 1 --colorScheme \"dark\" --viewport \"1920x1080\" # Emulate color scheme and viewport (resets other settings)\nchrome-devtools emulate 1 --userAgent \"Mozilla/5.0...\" # Emulate user agent (resets other settings)\nchrome-devtools resize_page 1 1920 1080 # Resizes the selected page's window\n```\n\n## Performance\n\n```bash\nchrome-devtools performance_analyze_insight 1 \"NAVIGATION_0\" \"LCPBreakdown\" # Get more details on a specific Performance Insight (pageId, insightSetId, insightName)\nchrome-devtools performance_start_trace 1 --reload true --autoStop false # Starts a performance trace recording (reload, autoStop)\nchrome-devtools performance_start_trace 1 --reload true --autoStop true --filePath \"t.json.gz\" # Start trace and save to a file\nchrome-devtools performance_stop_trace 1 # Stops the active performance trace\nchrome-devtools performance_stop_trace 1 --filePath \"t.json.gz\" # Stop trace and save to a file\n```\n\n## Memory\n\n```bash\nchrome-devtools take_heapsnapshot 1 \"./snap.heapsnapshot\" # Capture a memory heap snapshot\n```\n\n### Memory Debugging (requires `--memoryDebugging=true`)\n\n```bash\nchrome-devtools get_heapsnapshot_summary \"./snap.heapsnapshot\" # Get snapshot summary stats\nchrome-devtools compare_heapsnapshots \"./base.heapsnapshot\" \"./target.heapsnapshot\" # Compare two snapshots\nchrome-devtools get_heapsnapshot_details \"./snap.heapsnapshot\" # Snapshot statistics and per-class aggregates (with class IDs)\nchrome-devtools get_heapsnapshot_class_nodes \"./snap.heapsnapshot\" 18 # Inspect instances of a class (class ID from get_heapsnapshot_details)\nchrome-devtools get_heapsnapshot_dominators \"./snap.heapsnapshot\" 123 # Dominator tree for node\nchrome-devtools get_heapsnapshot_duplicate_strings \"./snap.heapsnapshot\" # Find duplicated strings\nchrome-devtools get_heapsnapshot_edges \"./snap.heapsnapshot\" 123 # Node edges/references\nchrome-devtools get_heapsnapshot_object_details \"./snap.heapsnapshot\" 123 # Object details by node ID\nchrome-devtools get_heapsnapshot_retainers \"./snap.heapsnapshot\" 123 # Retaining objects\nchrome-devtools get_heapsnapshot_retaining_paths \"./snap.heapsnapshot\" 123 # Shortest retaining paths\nchrome-devtools close_heapsnapshot \"./snap.heapsnapshot\" # Free memory from loaded snapshot\n```\n\n## Network\n\n```bash\nchrome-devtools get_network_request 1 # Get the currently selected network request for page 1\nchrome-devtools get_network_request 1 --reqid 1 --requestFilePath \"req.md\" # Get request by id and save to file\nchrome-devtools get_network_request 1 --reqid 1 --responseFilePath \"res.md\" # Save response body to file\nchrome-devtools list_network_requests 1 # List all network requests for page 1\nchrome-devtools list_network_requests 1 --pageSize 50 --pageIdx 0 # List network requests with pagination\nchrome-devtools list_network_requests 1 --resourceTypes fetch # Filter requests by resource type\nchrome-devtools list_network_requests 1 --includePreservedRequests true # Include preserved requests\n```\n\n## Debugging & Inspection\n\n```bash\nchrome-devtools evaluate_script \"() => document.title\" --pageId 1 # Evaluate a JavaScript function on page 1\nchrome-devtools evaluate_script \"(a) => a.innerText\" --pageId 1 --args 1_4 # Evaluate JS with UID arguments on page 1\nchrome-devtools evaluate_script --pageId 1 --sourcePath /path/to/script.js --format script # Evaluate a local classic JavaScript file on page 1\nchrome-devtools get_console_message 1 1 # Gets a console message by its ID\nchrome-devtools get_css_styles 1 \"1_4\" # Get CSS styles with pagination on page 1 (default: 10 rules, pageIdx 0)\nchrome-devtools get_css_styles 1 \"1_4\" --pageSize 20 --pageIdx 1 # Paginate CSS rules with custom page size and custom 0-based page index\nchrome-devtools lighthouse_audit 1 --mode \"navigation\" # Run Lighthouse audit for navigation\nchrome-devtools lighthouse_audit 1 --mode \"snapshot\" --device \"mobile\" # Run Lighthouse audit for a snapshot on mobile\nchrome-devtools lighthouse_audit 1 --outputDirPath ./out # Run Lighthouse audit and save reports\nchrome-devtools list_console_messages 1 # List all console messages\nchrome-devtools list_console_messages 1 --pageSize 20 --pageIdx 1 # List console messages with pagination\nchrome-devtools list_console_messages 1 --types error --types info # Filter console messages by type\nchrome-devtools list_console_messages 1 --includePreservedMessages true # Include preserved messages\nchrome-devtools take_screenshot 1 # Take a screenshot of the page viewport\nchrome-devtools take_screenshot 1 --fullPage true --format \"jpeg\" --quality 80 # Take a full page screenshot as JPEG with quality\nchrome-devtools take_screenshot 1 --uid \"id\" --filePath \"s.png\" # Take a screenshot of an element\nchrome-devtools take_snapshot 1 # Take a text snapshot of the page from the a11y tree\nchrome-devtools take_snapshot 1 --verbose true --filePath \"s.txt\" # Take a verbose snapshot and save to file\nchrome-devtools screencast_start 1 --filePath \"screen.mp4\" # Starts an mp4 screencast recording on page 1\nchrome-devtools screencast_stop 1 # Stops the active screencast on page 1\n```\n\n## Extensions\n\n```bash\nchrome-devtools list_extensions # Lists all the Chrome extensions installed in the browser\nchrome-devtools install_extension \"/path/to/extension\" # Installs a Chrome extension from the given path\nchrome-devtools uninstall_extension \"extension_id\" # Uninstalls a Chrome extension by its ID\nchrome-devtools reload_extension \"extension_id\" # Reloads an unpacked Chrome extension by its ID\nchrome-devtools trigger_extension_action \"extension_id\" # Triggers the default action of an extension by its ID\n```\n\n## Progressive Web Apps (requires `--categoryPwa=true`)\n\n```bash\nchrome-devtools install_pwa \"https://example.com/\" \"https://example.com/\" # Install PWA by manifest ID and install URL\nchrome-devtools launch_pwa \"https://example.com/\" # Launch installed PWA\nchrome-devtools get_os_app_state \"https://example.com/\" # Get OS app installation state\nchrome-devtools uninstall_pwa \"https://example.com/\" # Uninstall PWA and close windows\n```\n\n## Experimental Features\n\nExperimental tools are disabled by default. Enable them with the corresponding flag during `start`.\n\n```bash\nchrome-devtools click_at 1 100 200 # Clicks at the provided coordinates on page 1 (requires --experimentalVision=true)\nchrome-devtools list_webmcp_tools 1 # List all WebMCP tools on page 1 (requires --categoryExperimentalWebmcp=true)\nchrome-devtools execute_webmcp_tool 1 \"tool_name\" --input '{\"arg\":\"val\"}' # Execute a WebMCP tool on page 1 (requires --categoryExperimentalWebmcp=true)\nchrome-devtools list_3p_developer_tools 1 # List third-party developer tools on page 1 (requires --categoryExperimentalThirdParty=true)\nchrome-devtools execute_3p_developer_tool 1 \"tool_name\" --params '{\"arg\":\"val\"}' # Execute third-party developer tool on page 1 (requires --categoryExperimentalThirdParty=true)\n```\n\n## Service Management\n\n```bash\nchrome-devtools start   # Start or restart chrome-devtools-mcp\nchrome-devtools start --headless=false # Start with visible browser window\nchrome-devtools status  # Checks if chrome-devtools-mcp is running\nchrome-devtools stop    # Stop chrome-devtools-mcp if any\n```\n","skills/chrome-devtools-cli/references/installation.md":"# Installation\n\nInstall the package globally to make the `chrome-devtools` command available. You only need to do this the first time you use it.\n\n```sh\nnpm i chrome-devtools-mcp@latest -g\nchrome-devtools status # check if install worked.\n```\n\n## Troubleshooting\n\n- **Command not found:** If `chrome-devtools` is not recognized, ensure your global npm `bin` directory is in your system's `PATH`. Restart your terminal or source your shell configuration file (e.g., `.bashrc`, `.zshrc`).\n- **Permission errors:** If you encounter `EACCES` or permission errors during installation, avoid using `sudo`. Instead, use a node version manager like `nvm`, or configure npm to use a different global directory.\n- **Old version running:** Run `chrome-devtools stop && npm uninstall -g chrome-devtools-mcp` before reinstalling, or ensure the latest version is being picked up by your path.\n","skills/chrome-devtools/SKILL.md":"---\nname: chrome-devtools\ndescription: Uses Chrome DevTools via MCP for efficient debugging, troubleshooting and browser automation. Use when debugging web pages, automating browser interactions, analyzing performance, or inspecting network requests. This skill does not apply to `--slim` mode (MCP configuration).\n---\n\n## Core Concepts\n\n**Browser lifecycle**: Browser starts automatically on first tool call using a persistent Chrome profile. Configure via CLI args in the MCP server configuration: `npx chrome-devtools-mcp@latest --help`.\nAddional tooling can be enabled by providing the following flags:\n\n- For extension tooling, use the `--categoryExtensions` flag.\n- For memory tooling, use the `--memoryDebugging` flag.\n\n**Page targeting**: Page-scoped tools require a `pageId` parameter to target a specific page. Use `list_pages` to see available pages and their IDs (e.g. `pageId: 1`), or use the ID returned when creating a page with `new_page`.\nNote: For `evaluate_script`, `pageId` is required when targeting pages. However, when `--categoryExtensions` is enabled, `pageId` is optional so you can pass `serviceWorkerId` instead to evaluate inside an extension background service worker.\n**Element interaction**: Use `take_snapshot` to get page structure with element `uid`s. Each element has a unique `uid` for interaction. If an element isn't found, take a fresh snapshot - the element may have been removed or the page changed.\n\n## Workflow Patterns\n\n### Before interacting with a page\n\n1. Navigate: `navigate_page` or `new_page`\n2. Wait: `wait_for` to ensure content is loaded if you know what you look for.\n3. Snapshot: `take_snapshot` with `pageId` to understand page structure\n4. Interact: Use element `uid`s from snapshot for `click`, `fill`, etc., passing the corresponding `pageId`.\n\n### Efficient data retrieval\n\n- Use `filePath` parameter for large outputs (screenshots, snapshots, traces)\n- Use pagination (`pageIdx`, `pageSize`) and filtering (`types`) to minimize data\n- Set `includeSnapshot: false` on input actions unless you need updated page state\n\n### Tool selection\n\n- **Automation/interaction**: `take_snapshot` (text-based, faster, better for automation)\n- **Visual inspection**: `take_screenshot` (when user needs to see visual state)\n- **CSS & Styling inspection**: Use `get_css_styles` to inspect matched rules, cascade, and CSS variables. Treat the output as authoritative and complete.\n- **Additional details**: Use `evaluate_script` for runtime DOM/JS data not available via `get_css_styles` or `take_snapshot`.\n\n### Parallel execution\n\nYou can send multiple tool calls in parallel, but maintain correct order: navigate → wait → snapshot → interact.\n\n### Testing an extension\n\n> **Before proceeding**: Extension tools (`install_extension`, `list_extensions`, etc.) are only available when the MCP server is started with the `--categoryExtensions` flag. If these tools are not in your tool list, stop and ask the user to update their MCP server configuration:\n>\n> ```json\n> {\n>   \"mcpServers\": {\n>     \"chrome-devtools\": {\n>       \"command\": \"npx\",\n>       \"args\": [\"chrome-devtools-mcp@latest\", \"--categoryExtensions\"]\n>     }\n>   }\n> }\n> ```\n>\n> After updating, the user must restart the MCP server (or their AI client) for the change to take effect.\n\n1. **Install**: Use `install_extension` with the path to the unpacked extension.\n2. **Identify**: Get the extension ID from the response or by calling `list_extensions`.\n3. **Trigger Action**: Use `trigger_extension_action` to open the popup or side panel if applicable.\n4. **Verify Service Worker**: Use `evaluate_script` with `serviceWorkerId` (omitting `pageId` and `args`) to check extension state or trigger background actions. When evaluating in a page, pass `pageId` (omitting `serviceWorkerId`).\n5. **Verify Page Behavior**: Navigate to a page where the extension operates and use `take_snapshot` to check if content scripts injected elements or modified the page correctly.\n\n## Troubleshooting\n\nIf `chrome-devtools-mcp` is insufficient, guide users to use Chrome DevTools UI:\n\n- https://developer.chrome.com/docs/devtools\n- https://developer.chrome.com/docs/devtools/ai-assistance\n\nIf there are errors launching `chrome-devtools-mcp` or Chrome, refer to https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/troubleshooting.md.\n","skills/cookie-debugging/SKILL.md":"---\nname: cookie-debugging\ndescription: Uses Chrome DevTools MCP for inspecting, debugging, and testing cookies, session state, authentication issues, and cookie consent compliance. Use when diagnosing 401/403 errors, authentication redirects, session expiration, Cookie/Set-Cookie header issues, cookie banner consent conformance, or third-party cookie/SameSite/Partitioned cookie warnings.\n---\n\n## Core Concepts\n\n**Page Targeting**: Page-scoped tools (`list_network_requests`, `get_network_request`, `list_console_messages`, `evaluate_script`, `navigate_page`, `take_snapshot`, `click`, `lighthouse_audit`, `close_page`, etc.) require a `pageId` parameter. Retrieve available page IDs using `list_pages` or from `new_page`.\n\n### HttpOnly vs Client-Side Storage\n\nCookies marked `HttpOnly` cannot be accessed or modified by client-side JavaScript (`cookieStore` or `document.cookie`). However, the browser **automatically attaches active HttpOnly cookies to outgoing HTTP request headers (`Cookie`)**.\n\n- To inspect current `HttpOnly` values: Look at the `Cookie` request header of any outgoing HTTP request via `get_network_request`.\n- To inspect how cookies were created or configured: Look at the `Set-Cookie` response header of login/auth responses.\n- To inspect non-`HttpOnly` cookies: Use `evaluate_script` with the modern `cookieStore` API (`async () => await cookieStore.getAll()`).\n\n### Session Strategy: Live Tab vs Isolated Context\n\nChoose the right session environment to avoid state contamination (e.g., residual analytics or auth tokens):\n\n| Strategy                            | When to Use                                                                   | Setup / Teardown                                                                                              |\n| :---------------------------------- | :---------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------ |\n| **Live Tab (Active Page)**          | Diagnosing an active user session, live 401/403 error, or current state.      | Operates on the user's existing page; get its `pageId` from `list_pages`.                                     |\n| **Clean-Slate (`isolatedContext`)** | Testing cookie consent banners, first-time visits, or zero-cookie guarantees. | Call `new_page` with a unique `isolatedContext` (e.g. `\"consent-audit-1\"`). When finished, call `close_page`. |\n\n### Client-Side Capabilities & Limitations\n\n| Action                                                           | Client JavaScript (`cookieStore` / `document.cookie`) | DevTools Network & Context Tools                        |\n| :--------------------------------------------------------------- | :---------------------------------------------------- | :------------------------------------------------------ |\n| **Read Non-HttpOnly**                                            | ✅ `async () => await cookieStore.getAll()`           | ✅ `get_network_request` (Request `Cookie`)             |\n| **Read HttpOnly**                                                | ❌ Blocked by browser security                        | ✅ `get_network_request` (Request `Cookie`)             |\n| **Inspect Attributes** (`Domain`, `Path`, `SameSite`, `Expires`) | ✅ `async () => await cookieStore.getAll()`           | ✅ `get_network_request` (Response `Set-Cookie`)        |\n| **Modify / Delete Non-HttpOnly**                                 | ✅ `async () => await cookieStore.set(...)`           | N/A                                                     |\n| **Modify / Delete HttpOnly**                                     | ❌ **Silent failure** in JavaScript                   | ✅ Use `new_page(isolatedContext: ...)` for clean state |\n\n> [!WARNING]\n> Attempting to clear an `HttpOnly` cookie via JavaScript (`cookieStore.delete` or `document.cookie = \"...; max-age=0\"`) will silently fail. To test in an unauthenticated or fresh state, always spawn a new isolated context using `new_page` with `isolatedContext`.\n\n---\n\n## Workflow Patterns\n\n### 1. Diagnosing Authentication Failures & Redirects (401 / 403)\n\nWhen an authenticated page request fails, returns 401/403, or redirects to login:\n\n1. **List Recent Requests**: Call `list_network_requests` with `includePreservedRequests: true`.\n2. **Find the Target Request**: Locate the failing request (401/403) or redirect (302/307).\n3. **Inspect Outgoing `Cookie` Header**: Call `get_network_request` with the `reqid`.\n   - Verify if the `Cookie` header was attached and whether required tokens (e.g. `SESSION_ID`, `auth_token`) were sent.\n4. **Trigger Active Inspection (If no recent request exists)**:\n   - If the cookie was set in a previous session and no network call is listed, trigger a request:\n     - Use `navigate_page` with `type: \"reload\"`, OR\n     - Call `evaluate_script` with `() => fetch(window.location.href)`\n   - Then call `get_network_request` on the new request to inspect the active `Cookie` header.\n5. **Trace the Setting Request**: If the cookie is missing or rejected:\n   - Check earlier login/handshake responses for `Set-Cookie` directives:\n     - **Path mismatch**: e.g., `Path=/api` when the request is to `/`.\n     - **Domain mismatch**: e.g., `Domain=api.example.com` preventing cookies on `sub.example.com`.\n     - **Secure flag on HTTP**: `Secure` cookies are never sent over unencrypted `http://`.\n     - **SameSite blocking**: `SameSite=Strict` cookies are omitted on cross-site navigations.\n     - **Expiration**: Check if `Expires` or `Max-Age` elapsed.\n\n### 2. Cookie Banner & Consent Conformance Testing\n\nTo verify that no non-essential or tracking cookies are set before consent or when declining:\n\n1. **Start Clean**: Open a fresh isolated context with a dedicated name:\n   ```json\n   {\"url\": \"<PAGE_URL>\", \"isolatedContext\": \"consent-test-1\"}\n   ```\n2. **Record Baseline Cookies**: Before interacting with the banner, run `evaluate_script` with `async () => await cookieStore.getAll()`.\n3. **Inspect Premature Network Requests & Issues**:\n   - Call `list_network_requests` to ensure no third-party tracking beacons fired before consent.\n   - Call `list_console_messages` with `types: [\"issue\"]` to check for tracking warnings.\n4. **Interact with Consent Banner**:\n   - Capture snapshot with `take_snapshot` to locate the \"Decline\" or \"Reject All\" button `uid`.\n   - Click the button with `click`.\n5. **Verify Cookie Difference**:\n   - Run `evaluate_script` with `async () => await cookieStore.getAll()` after clicking to assert that only strictly necessary or consent-state cookies exist.\n6. **Test Consent Revocation (Lifecycle Audit)**:\n   - When auditing consent withdrawal or preference changes:\n     - Locate and click the \"Cookie Settings\", \"Manage Preferences\", or footer privacy trigger (`take_snapshot` $\\rightarrow$ `click`).\n     - Deselect non-essential categories or click \"Revoke All\" / \"Save Preferences\".\n     - Re-query `cookieStore.getAll()` to verify previously accepted non-essential cookies were cleared or expired.\n     - Call `list_network_requests` on subsequent actions to ensure tracking beacons are no longer fired.\n7. **Teardown Context**: Call `close_page` when the audit is complete to prevent leftover cookies from affecting subsequent tasks.\n\n### 3. Auditing Cookie Security, SameSite & CHIPS (Partitioned Cookies)\n\n1. **Fast-Track: Native DevTools Issues (Recommended)**:\n   - Call `list_console_messages` with:\n     ```json\n     {\n       \"pageId\": 1,\n       \"types\": [\"issue\"],\n       \"includePreservedMessages\": true\n     }\n     ```\n   - Check for `CookieIssue` entries, such as:\n     - `SameSiteNoneInsecure`: `SameSite=None` without `Secure`.\n     - `ThirdPartyCookiePhaseout`: Third-party cookie blocked or restricted.\n     - `SchemefulSameSite`: Cross-scheme cookie issues.\n     - `PartitionedCookies`: Invalid CHIPS partitioning attributes.\n2. **Deep Audit: Lighthouse Third-Party Cookies**:\n   - Run `lighthouse_audit` with `mode: \"navigation\"` (omit `outputDirPath` to use the default temporary files, or specify a directory within the workspace).\n   - **Extract the specific cookie audit** from the returned `report.json` path without loading the full report into context:\n     ```bash\n     node -e \"const r=require('<path/to/report.json>'); const a=r.audits['third-party-cookies']; console.log(JSON.stringify({score: a?.score, displayValue: a?.displayValue, items: a?.details?.items}))\"\n     ```\n\n### 4. Client-Side Cookie Inspection & Manipulation\n\nFor client-accessible, non-`HttpOnly` cookies (e.g., UI preferences, non-sensitive feature flags):\n\n1. **Read Cookies & Attributes**:\n   - Use the modern asynchronous Cookie Store API:\n     ```js\n     async () => await cookieStore.getAll();\n     ```\n   - _Fallback for insecure HTTP origins_: `() => document.cookie`.\n2. **Set / Modify Cookie**:\n   - Set client cookie via `cookieStore`:\n     ```js\n     async () =>\n       await cookieStore.set({\n         name: 'theme',\n         value: 'dark',\n         expires: Date.now() + 86400000,\n         sameSite: 'lax',\n       });\n     ```\n3. **Delete Cookie**:\n   - Clear client cookie:\n     ```js\n     async () => await cookieStore.delete('theme');\n     ```\n\n---\n\n## Troubleshooting\n\n- **`cookieStore` is undefined**: `cookieStore` requires a Secure Context (`https://`, `localhost`, or `127.0.0.1`). On non-secure HTTP origins, use `() => document.cookie` or test over HTTPS.\n- **`evaluate_script` returns empty / unresolved Promise**: `cookieStore` methods are asynchronous. Always wrap calls with `async () => await cookieStore.getAll()`.\n- **Cookie not visible in JavaScript**: The cookie is marked `HttpOnly`. Trigger a network request and call `get_network_request` to view it in the `Cookie` request header.\n- **JavaScript deletion did not remove cookie**: The cookie is `HttpOnly` or requires matching `Path` and `Domain` parameters. Use a fresh `isolatedContext` with `new_page` for a clean slate.\n- **Cookie set in response but not sent in requests**:\n  - Verify if page is `http://` while cookie specifies `Secure`.\n  - Check if `Domain` restricts subdomains.\n  - Check `list_console_messages(types: [\"issue\"])` for browser rejection reasons.\n- **Residual cookies contaminating audits**: Always use `new_page` with a unique `isolatedContext` when running compliance tests, and call `close_page` when done.\n","skills/debug-optimize-lcp/SKILL.md":"---\nname: debug-optimize-lcp\ndescription: Guides debugging and optimizing Largest Contentful Paint (LCP) using Chrome DevTools MCP tools. Use this skill whenever the user asks about LCP performance, slow page loads, Core Web Vitals optimization, or wants to understand why their page's main content takes too long to appear. Also use when the user mentions \"largest contentful paint\", \"page load speed\", \"CWV\", or wants to improve how fast their hero image or main content renders.\n---\n\n## What is LCP and why it matters\n\nLargest Contentful Paint (LCP) measures how quickly a page's main content becomes visible. It's the time from navigation start until the largest image or text block renders in the viewport.\n\n- **Good**: 2.5 seconds or less\n- **Needs improvement**: 2.5–4.0 seconds\n- **Poor**: greater than 4.0 seconds\n\nLCP is a Core Web Vital that directly affects user experience and search ranking. On 73% of mobile pages, the LCP element is an image.\n\n## LCP Subparts Breakdown\n\nEvery page's LCP breaks down into four sequential subparts with no gaps or overlaps. Understanding which subpart is the bottleneck is the key to effective optimization.\n\n| Subpart                       | Ideal % of LCP | What it measures                               |\n| ----------------------------- | -------------- | ---------------------------------------------- |\n| **Time to First Byte (TTFB)** | ~40%           | Navigation start → first byte of HTML received |\n| **Resource load delay**       | <10%           | TTFB → browser starts loading the LCP resource |\n| **Resource load duration**    | ~40%           | Time to download the LCP resource              |\n| **Element render delay**      | <10%           | LCP resource downloaded → LCP element rendered |\n\nThe \"delay\" subparts should be as close to zero as possible. If either delay subpart is large relative to the total LCP, that's the first place to optimize.\n\n**Common Pitfall**: Optimizing one subpart (like compressing an image to reduce load duration) without checking others. If render delay is the real bottleneck, a smaller image won't help — the saved time just shifts to render delay.\n\n## Debugging Workflow\n\nFollow these steps in order. Each step builds on the previous one.\n\n### Step 1: Record a Performance Trace\n\nNavigate to the page, then record a trace with reload to capture the full page load including LCP:\n\n1. `navigate_page` with `pageId` to the target URL.\n2. `performance_start_trace` with `pageId`, `reload: true` and `autoStop: true`.\n\nThe trace results will include LCP timing and available insight sets. Note the insight set IDs from the output — you'll need them in the next step.\n\n### Step 2: Analyze LCP Insights\n\nUse `performance_analyze_insight` to drill into LCP-specific insights. Look for these insight names in the trace results:\n\n- **LCPBreakdown** — Shows the four LCP subparts with timing for each.\n- **DocumentLatency** — Server response time issues affecting TTFB.\n- **RenderBlocking** — Resources blocking the LCP element from rendering.\n- **LCPDiscovery** — Whether the LCP resource was discoverable early.\n\nCall `performance_analyze_insight` with `pageId`, the insight set ID, and the insight name from the trace results.\n\n### Step 3: Identify the LCP Element\n\nUse `evaluate_script` (with `pageId`) and the **\"Identify LCP Element\" snippet** found in [references/lcp-snippets.md](references/lcp-snippets.md) to reveal the LCP element's tag, resource URL, and raw timing data.\n\nThe `url` field tells you what resource to look for in the network waterfall. If `url` is empty, the LCP element is text-based (no resource to load).\n\n### Step 4: Check the Network Waterfall\n\nUse `list_network_requests` to see when the LCP resource loaded relative to other resources:\n\n- Call `list_network_requests` with `pageId` filtered by `resourceTypes: [\"image\", \"font\"]` (adjust based on Step 3).\n- Then use `get_network_request` with `pageId` and the LCP resource's request ID for full details.\n\n**Key Checks:**\n\n- **Start Time**: Compare against the HTML document and the first resource. If the LCP resource starts much later than the first resource, there's resource load delay to eliminate.\n- **Duration**: A large resource load duration suggests the file is too big or the server is slow.\n\n### Step 5: Inspect HTML for Common Issues\n\nUse `evaluate_script` (with `pageId`) and the **\"Audit Common Issues\" snippet** found in [references/lcp-snippets.md](references/lcp-snippets.md) to check for lazy-loaded images in the viewport, missing fetchpriority, and render-blocking scripts.\n\n## Optimization Strategies\n\nAfter identifying the bottleneck subpart, apply these prioritized fixes.\n\n### 1. Eliminate Resource Load Delay (target: <10%)\n\nThe most common bottleneck. The LCP resource should start loading immediately.\n\n- **Root Cause**: LCP image loaded via JS/CSS, `data-src` usage, or `loading=\"lazy\"`.\n- **Fix**: Use standard `<img>` with `src`. **Never** lazy-load the LCP image.\n- **Fix**: Add `<link rel=\"preload\" fetchpriority=\"high\">` if the image isn't discoverable in HTML.\n- **Fix**: Add `fetchpriority=\"high\"` to the LCP `<img>` tag.\n\n### 2. Eliminate Element Render Delay (target: <10%)\n\nThe element should render immediately after loading.\n\n- **Root Cause**: Large stylesheets, synchronous scripts in `<head>`, or main thread blocking.\n- **Fix**: Inline critical CSS, defer non-critical CSS/JS.\n- **Fix**: Break up long tasks blocking the main thread.\n- **Fix**: Use Server-Side Rendering (SSR) so the element exists in initial HTML.\n\n### 3. Reduce Resource Load Duration (target: ~40%)\n\nMake the resource smaller or faster to deliver.\n\n- **Fix**: Use modern formats (WebP, AVIF) and responsive images (`srcset`).\n- **Fix**: Serve from a CDN.\n- **Fix**: Set `Cache-Control` headers.\n- **Fix**: Use `font-display: swap` if LCP is text blocked by a web font.\n\n### 4. Reduce TTFB (target: ~40%)\n\nThe HTML document itself takes too long to arrive.\n\n- **Fix**: Minimize redirects and optimize server response time.\n- **Fix**: Cache HTML at the edge (CDN).\n- **Fix**: Ensure pages are eligible for back/forward cache (bfcache).\n\n## Verifying Fixes & Emulation\n\n- **Verification**: Re-run the trace (`performance_start_trace` with `pageId` and `reload: true`) and compare the new subpart breakdown. The bottleneck should shrink.\n- **Emulation**: Lab measurements differ from real-world experience. Use `emulate` to test under constraints:\n  - `emulate` with `pageId`, `networkConditions: \"Fast 3G\"` and `cpuThrottlingRate: 4`.\n  - This surfaces issues visible only on slower connections/devices.\n","skills/debug-optimize-lcp/references/elements-and-size.md":"# Elements and Size for LCP\n\n## What Elements are Considered?\n\nThe types of elements considered for Largest Contentful Paint (LCP) are:\n\n- **`<img>` elements**: The first frame presentation time is used for animated content like GIFs.\n- **`<image>` elements** inside an `<svg>` element.\n- **`<video>` elements**: The poster image load time or first frame presentation time, whichever is earlier.\n- **Background images**: Elements with a background image loaded using `url()`.\n- **Block-level elements**: Containing text nodes or other inline-level text element children.\n\n## Heuristics to Exclude Non-Contentful Elements\n\nChromium-based browsers use heuristics to exclude:\n\n- Elements with **opacity of 0**.\n- Elements that **cover the full viewport** (likely background).\n- **Placeholder images** or low-entropy images.\n\n## How is an Element's Size Determined?\n\n- **Visible Area**: Typically the size visible within the viewport. Extending outside, clipped, or overflow portions don't count.\n- **Image Elements**: Either the visible size or the intrinsic size, whichever is smaller.\n- **Text Elements**: The smallest rectangle containing all text nodes.\n- **Exclusions**: Margin, padding, and borders are not considered toward the size.\n- **Containment**: Every text node belongs to its closest block-level ancestor element.\n","skills/debug-optimize-lcp/references/lcp-breakdown.md":"# Largest Contentful Paint (LCP) Breakdown\n\nLCP measures the time from when the user initiates loading the page until the largest image or text block is rendered within the viewport. To provide a good user experience, sites should strive to have an LCP of 2.5 seconds or less for at least 75% of page visits.\n\n## The Four Subparts of LCP\n\nEvery page's LCP consists of these four subcategories. There's no gap or overlap between them, and they add up to the full LCP time.\n\n| LCP subpart                   | % of LCP (Optimal) | Description                                                                                                                                                              |\n| ----------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| **Time to First Byte (TTFB)** | ~40%               | The time from when the user initiates loading the page until the browser receives the first byte of the HTML document response.                                          |\n| **Resource load delay**       | <10%               | The time between TTFB and when the browser starts loading the LCP resource. If the LCP element doesn't require a resource load (e.g., system font text), this time is 0. |\n| **Resource load duration**    | ~40%               | The duration of time it takes to load the LCP resource itself. If the LCP element doesn't require a resource load, this time is 0.                                       |\n| **Element render delay**      | <10%               | The time between when the LCP resource finishes loading and the LCP element rendering fully.                                                                             |\n\n## Why the Breakdown Matters\n\nOptimizing for LCP requires identifying which of these subparts is the bottleneck:\n\n- **Large delta between TTFB and FCP**: Indicates the browser needs to download a lot of render-blocking assets or complete a lot of work (e.g., client-side rendering).\n- **Large delta between FCP and LCP**: Indicates the LCP resource is not immediately available for the browser to prioritize or the browser is completing other work before it can display the LCP content.\n- **Large resource load delay**: Indicates the resource is not discoverable early or is deprioritized.\n- **Large element render delay**: Indicates rendering is blocked by stylesheets, scripts, or long tasks.\n","skills/debug-optimize-lcp/references/lcp-snippets.md":"# LCP Debugging Snippets\n\nUse these JavaScript snippets with the `evaluate_script` tool to extract deep insights from the page.\n\n## 1. Identify LCP Element\n\nUse this snippet to identify the LCP element and get raw timing data from the Performance API.\n\n```javascript\nasync () => {\n  return await new Promise(resolve => {\n    new PerformanceObserver(list => {\n      const entries = list.getEntries();\n      const last = entries[entries.length - 1];\n      resolve({\n        element: last.element?.tagName,\n        id: last.element?.id,\n        className: last.element?.className,\n        url: last.url,\n        startTime: last.startTime,\n        renderTime: last.renderTime,\n        loadTime: last.loadTime,\n        size: last.size,\n      });\n    }).observe({type: 'largest-contentful-paint', buffered: true});\n  });\n};\n```\n\n## 2. Audit Common Issues\n\nUse this snippet to check for common DOM-based LCP issues (lazy loading, priority).\n\n```javascript\n() => {\n  const issues = [];\n\n  // Check for lazy-loaded images in viewport\n  document.querySelectorAll('img[loading=\"lazy\"]').forEach(img => {\n    const rect = img.getBoundingClientRect();\n    if (rect.top < window.innerHeight) {\n      issues.push({\n        issue: 'lazy-loaded image in viewport',\n        element: img.outerHTML.substring(0, 200),\n        fix: 'Remove loading=\"lazy\" from this image — it is in the initial viewport and may be the LCP element',\n      });\n    }\n  });\n\n  // Check for LCP-candidate images missing fetchpriority\n  document.querySelectorAll('img:not([fetchpriority])').forEach(img => {\n    const rect = img.getBoundingClientRect();\n    if (rect.top < window.innerHeight && rect.width * rect.height > 50000) {\n      issues.push({\n        issue: 'large viewport image without fetchpriority',\n        element: img.outerHTML.substring(0, 200),\n        fix: 'Add fetchpriority=\"high\" to this image — it is large and visible in the initial viewport',\n      });\n    }\n  });\n\n  // Check for render-blocking scripts in head\n  document\n    .querySelectorAll(\n      'head script:not([async]):not([defer]):not([type=\"module\"])',\n    )\n    .forEach(script => {\n      if (script.src) {\n        issues.push({\n          issue: 'render-blocking script in head',\n          element: script.outerHTML.substring(0, 200),\n          fix: 'Add async or defer attribute, or move to end of body',\n        });\n      }\n    });\n\n  return {issueCount: issues.length, issues};\n};\n```\n","skills/debug-optimize-lcp/references/optimization-strategies.md":"# LCP Optimization Strategies\n\n## 1. Eliminate Resource Load Delay\n\n**Goal**: Ensure the LCP resource starts loading as early as possible.\n\n- **Early Discovery**: Ensure the LCP resource is discoverable in the initial HTML document response (not dynamically added by JS or hidden in `data-src`).\n- **Preload**: Use `<link rel=\"preload\">` with `fetchpriority=\"high\"` for critical images or fonts.\n- **Avoid Lazy Loading**: Never set `loading=\"lazy\"` on the LCP image.\n- **Fetch Priority**: Use `fetchpriority=\"high\"` on the `<img>` tag.\n- **Same Origin**: Host critical resources on the same origin or use `<link rel=\"preconnect\">`.\n\n## 2. Eliminate Element Render Delay\n\n**Goal**: Ensure the LCP element can render immediately after its resource has finished loading.\n\n- **Minimize Render-Blocking CSS**: Inline critical CSS and defer non-critical CSS. Ensure the stylesheet is smaller than the LCP resource.\n- **Minimize Render-Blocking JS**: Avoid synchronous scripts in the `<head>`. Inline very small scripts.\n- **Server-Side Rendering (SSR)**: Deliver the full HTML markup from the server so image resources are discoverable immediately.\n- **Break Up Long Tasks**: Prevent large JavaScript tasks from blocking the main thread during rendering.\n\n## 3. Reduce Resource Load Duration\n\n**Goal**: Reduce the time spent transferring the bytes of the resource.\n\n- **Optimize Resource Size**: Serve optimal image sizes, use modern formats (AVIF, WebP), and compress images/fonts.\n- **Geographic Proximity (CDN)**: Use a Content Delivery Network to get servers closer to users.\n- **Reduce Contention**: Use `fetchpriority=\"high\"` to prevent lower-priority resources from competing for bandwidth.\n- **Caching**: Use efficient `Cache-Control` policies.\n\n## 4. Reduce Time to First Byte (TTFB)\n\n**Goal**: Deliver the initial HTML as quickly as possible.\n\n- **Minimize Redirects**: Avoid multiple redirects from advertisements or shortened links.\n- **CDN Caching**: Cache static HTML documents at the edge.\n- **Edge Computing**: Move dynamic logic to the edge to avoid trips to the origin server.\n- **Back/Forward Cache**: Ensure pages are eligible for bfcache.\n","skills/memory-leak-debugging/SKILL.md":"---\nname: memory-leak-debugging\ndescription: Diagnoses and resolves memory leaks in JavaScript/Node.js applications. Use when a user reports high memory usage, OOM errors, or wants to capture, compare, or inspect heap snapshots with Chrome DevTools MCP memory tools.\n---\n\n# Memory Leak Debugging\n\nThis skill provides expert guidance and workflows for finding, diagnosing, and fixing memory leaks in JavaScript and Node.js applications using Chrome DevTools MCP tools.\n\n## Prerequisites\n\nAdvanced memory debugging tools (`compare_heapsnapshots`, `get_heapsnapshot_details`, etc.) are only available when the server is started with the `--memoryDebugging` flag. First check if these tools are available; if not, try to read the MCP configuration file to check if `--memoryDebugging` is enabled.\n\n## Core Principles\n\n- **Prefer MCP memory tools:** Do NOT attempt to read raw `.heapsnapshot` files directly, as they are extremely large and will consume too many tokens. Use the Chrome DevTools MCP heap snapshot tools to summarize, compare, and inspect snapshots.\n- **Isolate the Leak:** Determine if the leak is in the browser (client-side) or Node.js (server-side).\n- **Common Culprits:** Look for detached DOM nodes, unhandled closures, global variables, event listeners not being removed, and caches growing unbounded. _Note: Detached DOM nodes are sometimes intentional caches; always ask the user before nulling them._\n- **Close Loaded Snapshots:** Heap snapshots can be large. After completing an investigation, use `close_heapsnapshot` for each loaded snapshot to release memory held by the MCP server.\n\n## Workflows\n\n### 1. Capturing Snapshots\n\nWhen investigating a frontend web application memory leak, utilize the `chrome-devtools-mcp` tools to interact with the application and take snapshots.\n\n- Use page-scoped tools like `click`, `navigate_page`, `fill`, etc. (specifying `pageId`) to manipulate the page into the desired state.\n- Revert the page back to the original state after interactions to see if memory is released.\n- Repeat the same user interactions 10 times to amplify the leak.\n- Use `take_heapsnapshot` (with `pageId`) to save `.heapsnapshot` files to disk at baseline, target (after actions), and final (after reverting actions) states.\n\n### 2. Comparing Snapshots\n\nOnce you have generated `.heapsnapshot` files using `take_heapsnapshot`, compare them with Chrome DevTools MCP memory tools.\n\n- Start with `get_heapsnapshot_summary` for each snapshot to confirm that the files load and to compare high-level totals.\n- Use `compare_heapsnapshots` to compare baseline and target snapshots. Start without `classIndex` for the summary diff, then request detailed class diffs only for suspicious growth by specifying `classIndex`.\n- Use the summary output from `compare_heapsnapshots` before drilling into specific node IDs.\n\n### 3. Inspecting Retainers and Dominator Chains\n\nWhen a class or object type grows unexpectedly, inspect the retaining chain and dominators with the MCP tools before changing code.\n\n- Use `get_heapsnapshot_class_nodes` to list instances of the suspicious class.\n- Use `get_heapsnapshot_retainers`, `get_heapsnapshot_retaining_paths`, `get_heapsnapshot_dominators`, and `get_heapsnapshot_edges` to understand why representative nodes are still reachable.\n- Use `get_heapsnapshot_object_details` with a specific `nodeId` to retrieve detailed object metadata (size, type, distance, and DOM detachedness).\n- Use `get_heapsnapshot_duplicate_strings` when string growth dominates the diff.\n- Read [references/common-leaks.md](references/common-leaks.md) for examples of common memory leaks and how to fix them after the retaining path points at application code.\n\n### 4. Advanced Analysis and Categorized Filters\n\nUse built-in MCP memory tools and filters to pinpoint specific leak categories directly without external tools.\n\n- Use `get_heapsnapshot_details` or `get_heapsnapshot_class_nodes` with `filterName` to target common leak causes:\n  - `objectsRetainedByDetachedDomNodes`: Identifies detached DOM elements retained in memory.\n  - `objectsRetainedByEventHandlers`: Identifies objects kept alive by unremoved event listeners.\n  - `objectsRetainedByContexts`: Identifies objects trapped in closures or execution contexts.\n  - `objectsRetainedByConsole`: Identifies objects retained by console logging.\n","skills/memory-leak-debugging/references/common-leaks.md":"# Common Memory Leaks\n\nWhen analyzing retaining paths, dominator chains, or class diffs with Chrome DevTools MCP memory tools, look for these common patterns in the codebase:\n\n## 1. Uncleared Event Listeners\n\nEvent listeners attached to global objects (like `window` or `document`) or long-living objects prevent garbage collection of the objects referenced in their callbacks.\n\n**Fix:** Always call `removeEventListener` when a component unmounts or the listener is no longer needed.\n\n## 2. Detached DOM Nodes\n\nA DOM node is removed from the document tree but is still referenced by a JavaScript variable. While detachedness is a good signal for a memory leak, it's not always a bug. For example, websites sometimes intentionally cache detached navigation trees.\n\n**Fix:** Signal the detached nodes to the user first. **Ask the user first** before nulling the references or changing the code, as the detached nodes might be part of an intentional cache. If confirmed as a leak, ensure variables holding DOM references are set to `null` when the node is removed, or limit their scope.\n\n## 3. Unintentional Global Variables\n\nVariables declared without `var`, `let`, or `const` (in non-strict mode) or explicitly attached to `window` remain in memory forever.\n\n**Fix:** Use strict mode, properly declare variables, and avoid global state.\n\n## 4. Closures\n\nClosures can unintentionally keep references to large objects in their outer scope.\n\n**Fix:** Nullify large objects when they are no longer needed, or refactor the closure to not capture unnecessary variables.\n\n## 5. Unbounded Caches or Arrays\n\nData structures used for caching (like objects, Arrays, or Maps) that grow without limits.\n\n**Fix:** Implement caching limits, use LRU caches, or use `WeakMap`/`WeakSet` for data associated with object lifecycles.\n","skills/troubleshooting/SKILL.md":"---\nname: troubleshooting\ndescription: Uses Chrome DevTools MCP and documentation to troubleshoot connection and target issues. Trigger this skill when list_pages, new_page, or navigate_page fail, or when the server initialization fails.\n---\n\n## Troubleshooting Wizard\n\nYou are acting as a troubleshooting wizard to help the user configure and fix their Chrome DevTools MCP server setup. When this skill is triggered (e.g., because `list_pages`, `new_page`, or `navigate_page` failed, or the server wouldn't start), follow this step-by-step diagnostic process:\n\n### Step 1: Find and Read Configuration\n\nYour first action should be to locate and read the MCP configuration file. Search for the following files in the user's workspace: `.mcp.json`, `.vscode/mcp.json`, `gemini-extension.json`, or `.gemini/settings.json` (or user-scoped files such as `~/.claude.json` if accessible).\n\nIf you find a configuration file, read and interpret it to identify potential issues such as:\n\n- Incorrect arguments or flags.\n- Missing environment variables.\n- Usage of `--autoConnect` in incompatible environments.\n\nIf you cannot find any of these files, only then should you ask the user to provide their configuration file content.\n\n### Step 2: Triage Common Connection Errors\n\nBefore reading documentation or suggesting configuration changes, check if the error message matches one of the following common patterns.\n\n#### Error: `Could not find DevToolsActivePort`\n\nThis error is highly specific to the `--autoConnect` feature. It means the MCP server cannot find the file created by a running, debuggable Chrome instance. This is not a generic connection failure.\n\nYour primary goal is to guide the user to ensure Chrome is running and properly configured. Do not immediately suggest switching to `--browserUrl`. Follow this exact sequence:\n\n1. **Ask the user to confirm that the correct Chrome version** (e.g., \"Chrome Canary\" if the error mentions it) is currently running.\n2. **If the user confirms it is running, instruct them to enable remote debugging.** Be very specific about the URL and the action: \"Please open a new tab in Chrome, navigate to `chrome://inspect/#remote-debugging`, and make sure the 'Enable remote debugging' checkbox is checked.\"\n3. **Once the user confirms both steps, your only next action should be to call the `list_pages` tool.** This is the simplest and safest way to verify if the connection is now successful. Do not retry the original, more complex command yet.\n4. **If `list_pages` succeeds, the problem is resolved.** If it still fails with the same error, then you can proceed to the more advanced steps like suggesting `--browserUrl` or checking for sandboxing issues.\n\n#### Symptom: Server starts but creates a new empty profile\n\nIf the server starts successfully but `list_pages` returns an empty list or creates a new profile instead of connecting to the existing Chrome instance, check for typos in the arguments.\n\n- **Check for flag typos:** For example, `--autoBronnect` instead of `--autoConnect`.\n- **Verify the configuration:** Ensure the arguments match the expected flags exactly.\n\n#### Symptom: Missing Tools / Only a few tools available\n\nIf the server starts successfully but only a limited subset of tools (like `list_pages`, `list_console_messages`, `get_console_message`, `list_network_requests`) are available, this is likely because the MCP client is enforcing a **read-only mode**.\n\nAll tools in `chrome-devtools-mcp` are annotated with `readOnlyHint: true` (for safe, non-modifying tools) or `readOnlyHint: false` (for tools that modify browser state, like `emulate`, `click`, `navigate_page`). To access the full suite of tools, the user must disable read-only mode in their MCP client (e.g., by exiting \"Plan Mode\" in Gemini CLI or adjusting their client's tool safety settings).\n\n#### Symptom: Extension tools are missing or extensions fail to load\n\nIf the tools related to extensions (like `install_extension`) are not available, or if the extensions you load are not functioning:\n\n1. **Check for the `--categoryExtensions` flag**: Ensure this flag is passed in the MCP server configuration to enable the extension category tools.\n\n#### Other Common Errors\n\nIdentify other error messages from the failed tool call or the MCP initialization logs:\n\n- `Target closed`\n- \"Tool not found\" (check if they are using `--slim` which only enables navigation, script evaluation and screenshot tools).\n- Missing `pageId`: Page-scoped tools require a `pageId` argument. Call `list_pages` to find active page IDs.\n- `ProtocolError: Network.enable timed out` or `The socket connection was closed unexpectedly`\n- `Error [ERR_MODULE_NOT_FOUND]: Cannot find module`\n- Any sandboxing or host validation errors.\n\n### Step 3: Read Known Issues\n\nRead the contents of https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/troubleshooting.md to map the error to a known issue. Pay close attention to:\n\n- Sandboxing restrictions (macOS Seatbelt, Linux containers).\n- WSL requirements.\n- `--autoConnect` handshakes, timeouts, and requirements (requires **running** Chrome 144+).\n\n### Step 4: Formulate a Configuration\n\nBased on the exact error and the user's environment (OS, MCP client), formulate the correct MCP configuration snippet. Check if they need to:\n\n- Pass `--browser-url=http://127.0.0.1:9222` instead of `--autoConnect` (e.g. if they are in a sandboxed environment like Claude Desktop).\n- Enable remote debugging in Chrome (`chrome://inspect/#remote-debugging`) and accept the connection prompt. **Ask the user to verify this is enabled if using `--autoConnect`.**\n- Add `--logFile <absolute_path_to_log_file>` to capture debug logs for analysis.\n- Increase `startup_timeout_ms` (e.g. to 20000) if using Codex on Windows.\n\n_If you are unsure of the user's configuration, ask the user to provide their current MCP server JSON configuration._\n\n### Step 5: Run Diagnostic Commands\n\nIf the issue is still unclear, run diagnostic commands to test the server directly:\n\n- Run `npx chrome-devtools-mcp@latest --help` to verify the installation and Node.js environment.\n- If you need more information, run `NODE_DEBUG=* npx chrome-devtools-mcp@latest --logFile=/tmp/cdm-test.log` to capture verbose logs. Analyze the output for errors.\n\n### Step 6: Check GitHub for Existing Issues\n\nIf https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/troubleshooting.md does not cover the specific error, check if the `gh` (GitHub CLI) tool is available in the environment. If so, search the GitHub repository for similar issues:\n`gh issue list --repo ChromeDevTools/chrome-devtools-mcp --search \"<error snippet>\" --state all`\n\nAlternatively, you can recommend that the user checks https://github.com/ChromeDevTools/chrome-devtools-mcp/issues and https://github.com/ChromeDevTools/chrome-devtools-mcp/discussions for help.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/ChromeDevTools/chrome-devtools-mcp/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"This repository contains an MCP server and CLI for Chrome DevTools.\n\n# Instructions\n\n- Use only scripts from `package.json` to run commands.\n- Use `npm run build` to run tsc and test build.\n- Use `npm run test` to build and run tests, run all tests to verify correctness.\n- Use `npm run test path-to-test.ts` to build and run a single test file, for example, `npm run test tests/McpContext.test.ts`.\n- Use `npm run format` to fix formatting and get linting errors.\n- Never modify `third_party/devtools-frontend` except for experimentation: it is a git submodule, a mirror of the actual codebase.\n\n## Rules for TypeScript\n\n- Do not use `any` type.\n- Do not use `as` keyword for type casting.\n- Do not use `!` operator for type assertion.\n- Do not use `// @ts-ignore` comments.\n- Do not use `// @ts-nocheck` comments.\n- Do not use `// @ts-expect-error` comments.\n- Prefer `for..of` instead of `forEach`.\n- Never type-check types that are already type safe (e.g. redundant `typeof` checks on statically typed variables).\n\n## Rules for Testing\n\n### Structure and Separation of Concerns\n\n- **Prefer mock-based unit tests over real-browser tests**: Do not use `withMcpContext` or launch a real browser unless the test genuinely requires real browser or DevTools protocol integration (e.g., live CDP events, browser lifecycle, secondary sessions). Puppeteer already tests browser behavior upstream; unit tests run faster and avoid browser overhead.\n- **Tool handler tests (`tests/tools/*.test.ts`)**:\n  - Test that the tool handler parses/validates parameters and invokes the corresponding methods on `page`, `context`, or `response` with the exact expected arguments.\n  - Do **not** reimplement business logic or state tracking inside mocks (e.g., do not simulate state changes in mock methods).\n- **Core class tests (e.g., `tests/McpPage.test.ts`)**:\n  - Test business logic by instantiating the real class under test with mocked dependencies (e.g., instantiate `new McpPage(...)` with a mocked Puppeteer page from `createMockPuppeteerPage()`).\n  - Assert that the class calls the underlying Puppeteer methods with the expected parameters.\n\n### Mocking Guidelines (`tests/mocks.ts`)\n\n- **Centralize mocks in `tests/mocks.ts`**: Keep all reusable mock factories in `tests/mocks.ts`. Import directly from `tests/mocks.ts` (do not re-export from `tests/utils.ts`).\n- **Use `sinon.createStubInstance(Class)`**: Do not hand-roll mock objects or define custom mock interfaces. Use `sinon.createStubInstance()` so all prototype methods are automatically stubbed.\n- **Typing**: Use `sinon.SinonStubbedInstance<Class>` for mock types (e.g., `MockMcpPage`, `MockMcpContext`, `MockMcpResponse`).\n- **Handler mocks helper**: For tool handlers, use `const {page, context, response} = createHandlerMocks();` from `tests/mocks.ts` to set up all three mocks in one call.\n- **Keep mocks generic**: Do not tailor mocks to a specific tool or test suite.\n- **Naming conventions**: Use `mock` rather than `fake` in helper and variable names (e.g., `createMockPuppeteerPage`, `createMockMcpPage`). Name the mocked Puppeteer page instance `pptrPage`.\n\n### Assertions and Sinon Best Practices\n\n- **Use `sinon.assert` methods**: Do not use Node's `assert.ok(stub.calledOnce)` or `assert.deepStrictEqual(stub.firstCall.args[0], ...)` to verify stub calls.\n- **Verify exact arguments**:\n  - Use `sinon.assert.calledOnceWithExactly(stub, ...args)` for single calls with exact arguments.\n  - Use `sinon.assert.calledWithExactly(stub.secondCall, ...args)` for subsequent calls.\n  - Use `sinon.assert.notCalled(stub)` to assert a method was not invoked.\n- **Clean up stubs**: `tests/setup.ts` runs `sinon.restore()` after every test. Do not add `afterEach(() => sinon.restore());` to test suites.\n\n### Test Cleanliness\n\n- Do not add redundant comments or verbose JSDoc for short, self-describing mock functions or tests.\n- Only test real scenarios; avoid testing redundant or artificial calls that cannot happen in real usage.\n- Use current year (2026) in copyright headers for new test files.\n\n# Pre-submit checklist\n\nComplete every item before you upload a PR or push a new revision.\n\n## Scope and PR description\n\n- [ ] The PR contains one change. Move unrelated refactors, renames,\n      formatting changes, and build or compiler changes into their own PR.\n- [ ] The PR title, the commit type (`fix:`, `feat:`, `chore:`), and the\n      squash commit message describe the same change. A change to a default\n      value or to tool output is a `fix:` or a `feat:`, not a `chore:`.\n- [ ] The PR description lists every user-visible behavior change, including\n      changes to other tools that share the changed code path.\n- [ ] The PR description matches the shipped behavior. Remove claims about\n      fixes that the diff does not contain.\n- [ ] The PR description names new functions that have no production caller\n      yet, or the PR removes them.\n- [ ] The branch has no merge conflicts with `main`. Rebase only when a\n      conflict exists.\n- [ ] `npm run gen` ran after a change to a tool schema, a tool description,\n      or a CLI option. The generated files are committed.\n- [ ] `npm run format` ran. No line is longer than 80 columns.\n- [ ] `npm run test` passed locally. CI failures on all platforms are your\n      responsibility.\n\n## Design\n\n- [ ] The change lives in the lowest layer that owns the data: page logic in\n      `McpPage`, context logic in `McpContext`, formatting in\n      `src/formatters/*`. `McpResponse` does not resolve pages or workers.\n- [ ] If Puppeteer or devtools-frontend owns the bug, fix it there first. Do\n      not copy or re-implement upstream code in this repository.\n- [ ] Puppeteer internals are imported only through `src/third_party/index.ts`.\n- [ ] Repeated code is one helper. Extract a helper when the same block\n      appears two or more times. Prefer an existing helper or a Puppeteer\n      utility (for example `Deferred`) over a new one.\n- [ ] A new formatter class exposes `toJSON()` and `toString()`, and\n      `toString()` uses the `toJSON()` output.\n- [ ] A function with three or more optional parameters takes one options\n      object. Do not pass positional `undefined` placeholders.\n- [ ] Each default value is declared in one place.\n- [ ] A new behavior is opt-in by default when a wrong classification fails\n      silently (for example a config option is restart-required unless the\n      author marks it reloadable).\n- [ ] An invalid combination of flags or options is rejected by the type\n      system where possible, not only by a comment.\n- [ ] No public method exists only to serve a test. Put shared helpers in\n      `src/utils/*`.\n- [ ] Parameter validation lives in the Zod schema (`refine`, `coerce`), not\n      in the handler. Validation runs before the handler changes any state.\n- [ ] Numeric validation matches what the consumer accepts. Use\n      `Number.isInteger` and a range check where CDP expects integers.\n\n## Correctness and edge cases\n\n- [ ] A failed external call does not leave stale state. Restore the previous\n      value when a call rejects.\n- [ ] An error path still produces the artifacts and partial results the\n      agent needs before it throws.\n- [ ] An async operation that a timeout can abandon checks that its target\n      (browser, page, config) is still current when it resumes.\n- [ ] A lock or guard is acquired and released exactly once. A helper that\n      releases a guard throws when the guard is already released.\n- [ ] Concurrent callbacks (watchers, reloads, event handlers) are\n      serialized. A transient failure does not trigger a callback.\n- [ ] A filesystem operation that races with another process tolerates the\n      race (`fs.rmSync(path, {force: true})` or `try/catch`), not\n      `existsSync` + an unguarded call.\n- [ ] A failed lookup falls back to the previous behavior. It does not turn\n      a healthy state into an error state.\n- [ ] No synchronous process spawn or blocking call runs in a polling loop\n      or in a hot function.\n- [ ] `dispose()` releases every resource the object created, and runs for\n      objects that are already closed.\n- [ ] Event listeners added for one action are removed when the action ends.\n- [ ] Cached page-side state is refreshed before use. The page can reload\n      between tool calls.\n- [ ] Behavior shared by all tools that act on the selected page also runs\n      for tools defined with `defineTool`, not only `definePageTool`.\n- [ ] String and URL parsing handles userinfo, escaped delimiters, ports,\n      pattern groups, and empty or whitespace-only input. Use the full\n      authority, not a heuristic on the first or last character.\n- [ ] Truncation uses `Intl.Segmenter` graphemes, not `string.slice`.\n- [ ] Secrets never reach logs. Redact headers, URL userinfo, and query\n      strings in every log line, including process startup arguments.\n- [ ] A filesystem path parameter is validated with `validatePath()` even\n      when the value parses as a URL. The error says what the parameter\n      expects.\n- [ ] A value that crosses a process boundary (daemon IPC, CLI) is\n      serialized with `JSON.stringify` when it is not a primitive.\n- [ ] An identifier is issued only for items that a tool can resolve later.\n- [ ] A dedup key never collapses for items without identifying fields.\n      Use a counter or skip such items.\n- [ ] An error message names the exact condition that failed, with the\n      relevant values (pid, timeout, URL, parameter name).\n- [ ] A thrown non-`Error` value keeps its content in the error message.\n      Treat objects with a string `message` like `Error`.\n- [ ] Optional chaining and `?? default` are not applied to values whose\n      type is non-optional.\n- [ ] Version-dependent behavior (Node, Chrome, OS) is tested on every\n      version the `engines` field allows, or the test does not depend on it.\n\n## Tests\n\n- [ ] Every new or changed function has a test that calls it. A function\n      with no caller in `src/` and no test is removed from the PR.\n- [ ] Every review request of the form \"add a test for X\" has a test whose\n      name includes X.\n- [ ] A new test for a bug fails on `main` without the fix. Remove\n      assertions that pass with and without the fix.\n- [ ] A `notCalled` assertion targets the first method the handler calls\n      after the step under test, not a method inside a callback that a stub\n      never runs.\n- [ ] A test for a selected branch also asserts that the other branch was\n      not taken.\n- [ ] Assertions use `calledOnceWithExactly` or\n      `calledWithExactly(stub.nthCall, ...)`. Use `calledOnceWithMatch` only\n      for arguments that cannot be exact (for example an `AbortSignal`) or\n      when an overloaded signature forces `sinon.match.same`.\n- [ ] Options that the handler derives from its input are asserted, not\n      only the call count.\n- [ ] Test names describe the asserted behavior. Rename the test when the\n      assertion changes.\n- [ ] `if (value) { assert... }` does not appear in tests. Use\n      `assert.ok(value)` before the dependent assertions.\n- [ ] Real-browser tests exist only for behavior that mocks cannot observe.\n      A test file contains at most one slow end-to-end run per scenario.\n- [ ] A real-browser test that counts requests filters `favicon.ico`.\n- [ ] A real-browser test that waits for CDP events waits on the session or\n      result of the code under test, not on a new `createCDPSession()`.\n- [ ] Every awaited event in a test has a bounded timeout. Positive-path\n      tests poll for the condition instead of a fixed sleep.\n- [ ] New reusable stubs and fixtures live in `tests/mocks.ts` and build on\n      `sinon.createStubInstance`. No hand-rolled fixture uses `as unknown as`.\n- [ ] Mock options that equal the default value are not passed.\n- [ ] Temporary directories use `using dir = createTempDir(...)` from\n      `tests/utils.ts`, not `fs.mkdtemp`.\n- [ ] A test never reads the developer's home directory and never calls\n      `process.exit`. Stub locators and pass `exitProcess = false`.\n- [ ] When a real-browser test is replaced with mocks, each branch the old\n      test covered still has a test.\n- [ ] A test does not repeat a round-trip that another test already covers.\n      Stop after the first assertion that proves the change.\n\n## Documentation and output\n\n- [ ] A tool description states the exact behavior, including when a\n      parameter is ignored or depends on another parameter.\n- [ ] Skills stay generic. Add one short note in the tool-selection section\n      instead of a feature-specific workflow.\n- [ ] Inline comments are one sentence. Remove comments that repeat an\n      existing TSDoc.\n- [ ] Response text that depends on a condition prints only when the\n      condition is true.\n","isInternal":false,"tokens":2981,"sizeBytes":12769},{"name":"a11y-snippets.md","path":"skills/a11y-debugging/references/a11y-snippets.md","rawUrl":"https://raw.githubusercontent.com/ChromeDevTools/chrome-devtools-mcp/HEAD/skills/a11y-debugging/references/a11y-snippets.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Accessibility Debugging Snippets\n\nUse these JavaScript snippets with the `evaluate_script` tool.\n\n## 1. Find Orphaned Form Inputs\n\nFinds form inputs that lack an associated label (no `label[for]`, `aria-label`, `aria-labelledby`, or wrapping `<label>`).\n\n```js\n() =>\n  Array.from(document.querySelectorAll('input, select, textarea'))\n    .filter(i => {\n      const hasId = i.id && document.querySelector(`label[for=\"${i.id}\"]`);\n      const hasAria =\n        i.getAttribute('aria-label') || i.getAttribute('aria-labelledby');\n      return !hasId && !hasAria && !i.closest('label');\n    })\n    .map(i => ({\n      tag: i.tagName,\n      id: i.id,\n      name: i.name,\n      placeholder: i.placeholder,\n    }));\n```\n\n## 2. Measure Tap Target Size\n\nReturns the bounding box dimensions of an element. Pass the element's `uid` from the snapshot as an argument to `evaluate_script`.\n\n```js\nel => {\n  const rect = el.getBoundingClientRect();\n  return {width: rect.width, height: rect.height};\n};\n```\n\n## 3. Check Color Contrast\n\nApproximates the contrast ratio between an element's text color and background color. Pass the element's `uid` to test against WCAG AA (4.5:1 for normal text, 3:1 for large text).\n\n**Note**: This uses a simplified algorithm and may not account for transparency, gradients, or background images. For production-grade auditing, consider injecting `axe-core`.\n\n```js\nel => {\n  function getRGB(colorStr) {\n    const match = colorStr.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/);\n    return match\n      ? [parseInt(match[1]), parseInt(match[2]), parseInt(match[3])]\n      : [255, 255, 255];\n  }\n  function luminance(r, g, b) {\n    const a = [r, g, b].map(function (v) {\n      v /= 255;\n      return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);\n    });\n    return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;\n  }\n\n  const style = window.getComputedStyle(el);\n  const fg = getRGB(style.color);\n  let bg = getRGB(style.backgroundColor);\n\n  const l1 = luminance(fg[0], fg[1], fg[2]);\n  const l2 = luminance(bg[0], bg[1], bg[2]);\n  const ratio = (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);\n\n  return {\n    color: style.color,\n    bg: style.backgroundColor,\n    contrastRatio: ratio.toFixed(2),\n  };\n};\n```\n\n## 4. Global Page Checks\n\nChecks document-level accessibility settings often missed in component testing.\n\n```js\n() => ({\n  lang:\n    document.documentElement.lang ||\n    'MISSING - Screen readers need this for pronunciation',\n  title: document.title || 'MISSING - Required for context',\n  viewport:\n    document.querySelector('meta[name=\"viewport\"]')?.content ||\n    'MISSING - Check for user-scalable=no (bad practice)',\n  reducedMotion: window.matchMedia('(prefers-reduced-motion: reduce)').matches\n    ? 'Enabled'\n    : 'Disabled',\n});\n```\n","isInternal":false,"tokens":767,"sizeBytes":2788},{"name":"SKILL.md","path":"skills/a11y-debugging/SKILL.md","rawUrl":"https://raw.githubusercontent.com/ChromeDevTools/chrome-devtools-mcp/HEAD/skills/a11y-debugging/SKILL.md","title":"a11y-debugging","category":"anthropic-skill","format":"markdown","content":"---\nname: a11y-debugging\ndescription: Uses Chrome DevTools MCP for accessibility (a11y) debugging and auditing based on web.dev guidelines. Use when testing semantic HTML, ARIA labels, focus states, keyboard navigation, tap targets, and color contrast.\n---\n\n## Core Concepts\n\n**Page Targeting**: Page-scoped tools (`take_snapshot`, `list_console_messages`, `evaluate_script`, `press_key`, `take_screenshot`, `lighthouse_audit`, etc.) require a `pageId` parameter. Retrieve available page IDs using `list_pages` or from `new_page`.\n\n**Accessibility Tree vs DOM**: Visually hiding an element (e.g., `CSS opacity: 0`) behaves differently for screen readers than `display: none` or `aria-hidden=\"true\"`. The `take_snapshot` tool returns the accessibility tree of the page, which represents what assistive technologies \"see\", making it the most reliable source of truth for semantic structure.\n\n**Reading web.dev documentation**: If you need to research specific accessibility guidelines (like `https://web.dev/articles/accessible-tap-targets`), you can append `.md.txt` to the URL (e.g., `https://web.dev/articles/accessible-tap-targets.md.txt`) to fetch the clean, raw markdown version. This is much easier to read!\n\n## Workflow Patterns\n\n### 1. Automated Audit (Lighthouse)\n\nStart by running a Lighthouse accessibility audit to get a comprehensive baseline. This tool provides a high-level score and lists specific failing elements with remediation advice.\n\n1.  Run the audit:\n    - Set `mode` to `\"navigation\"` to refresh the page and capture load issues.\n    - Omit `outputDirPath` to save reports to the default temporary directory (the exact path to `report.json` is returned under `Reports`), or set `outputDirPath` to a directory within the workspace.\n2.  **Analyze the Summary**:\n    - In the default text output, category scores use a 0-100 scale (100 is a perfect score), followed by `Passed: N` and `Failed: N` audit counts.\n    - If the server was started with `--experimentalStructuredContent`, inspect `summary.scores[].score` (0-1 scale) and `summary.audits.failed` in the structured content instead.\n3.  **Review the Report (CRITICAL)**:\n    - **Parsing**: Do not read the entire file line-by-line. Use a CLI tool like `jq` or a Node.js one-liner to filter for failures from the report path returned by `lighthouse_audit`:\n      ```bash\n      # Extract failing audits with their details (replace with report.json path from tool response)\n      node -e \"const r=require('<path/to/report.json>'); Object.values(r.audits).filter(a=>a.score!==null && a.score<1).forEach(a=>console.log(JSON.stringify({id:a.id, title:a.title, items:a.details?.items})))\"\n      ```\n    - This efficiently extracts the `selector` and `snippet` of failing elements without loading the full report into context.\n\n### 2. Browser Issues & Audits\n\nChrome automatically checks for common accessibility problems. Use `list_console_messages` to check for these native audits:\n\n- `types`: `[\"issue\"]`\n- `includePreservedMessages`: `true` (to catch issues that occurred during page load)\n\nThis often reveals missing labels, invalid ARIA attributes, and other critical errors without manual investigation.\n\n### 3. Semantics & Structure\n\nThe accessibility tree exposes the heading hierarchy and semantic landmarks.\n\n1.  Navigate to the page.\n2.  Use `take_snapshot` to capture the accessibility tree.\n3.  **Check Heading Levels**: Ensure heading levels (`h1`, `h2`, `h3`, etc.) are logical and do not skip levels. The snapshot will include heading roles.\n4.  **Content Reordering**: Verify that the DOM order (which drives the accessibility tree) matches the visual reading order. Use `take_screenshot` to inspect the visual layout and compare it against the snapshot structure to catch CSS floats or absolute positioning that jumbles the logical flow.\n\n### 4. Labels, Forms & Text Alternatives\n\n1.  Locate buttons, inputs, and images in the `take_snapshot` output.\n2.  Ensure interactive elements have an accessible name (e.g., a button should not just say `\"\"` if it only contains an icon).\n3.  **Orphaned Inputs**: Verify that all form inputs have associated labels. Use `evaluate_script` with the **\"Find Orphaned Form Inputs\" snippet** found in [references/a11y-snippets.md](references/a11y-snippets.md).\n4.  Check images for `alt` text.\n\n### 5. Focus & Keyboard Navigation\n\nTesting \"keyboard traps\" and proper focus management without visual feedback relies on tracking the focused element.\n\n1.  Use the `press_key` tool with `\"Tab\"` or `\"Shift+Tab\"` to move focus.\n2.  Use `take_snapshot` to capture the updated accessibility tree.\n3.  Locate the element marked as focused in the snapshot to verify focus moved to the expected interactive element.\n4.  If a modal opens, focus must move into the modal and \"trap\" within it until closed.\n\n### 6. Tap Targets and Visuals\n\nAccording to web.dev, tap targets should be at least 48x48 pixels with sufficient spacing. Since the accessibility tree doesn't show sizes, use `evaluate_script` with the **\"Measure Tap Target Size\" snippet** found in [references/a11y-snippets.md](references/a11y-snippets.md).\n\n_Pass the element's `uid` from the snapshot as an argument to `evaluate_script`._\n\n### 7. Color Contrast\n\nTo verify color contrast ratios, use the **\"Check Color Contrast\" snippet** found in [references/a11y-snippets.md](references/a11y-snippets.md) with `evaluate_script`.\n\n### 8. Global Page Checks\n\nVerify document-level accessibility settings often missed in component testing using the **\"Global Page Checks\" snippet** found in [references/a11y-snippets.md](references/a11y-snippets.md).\n\n## Troubleshooting\n\nIf standard a11y queries fail or the `evaluate_script` snippets return unexpected results:\n\n- **Visual Inspection**: If automated scripts cannot determine contrast (e.g., text over gradient images or complex backgrounds), use `take_screenshot` to capture the element. While models cannot measure exact contrast ratios from images, they can visually assess legibility and identify obvious issues.\n","frontmatter":{"name":"a11y-debugging","description":"Uses Chrome DevTools MCP for accessibility (a11y) debugging and auditing based on web.dev guidelines. Use when testing semantic HTML, ARIA labels, focus states, keyboard navigation, tap targets, and color contrast."},"isInternal":false,"tokens":1369,"sizeBytes":6042},{"name":"installation.md","path":"skills/chrome-devtools-cli/references/installation.md","rawUrl":"https://raw.githubusercontent.com/ChromeDevTools/chrome-devtools-mcp/HEAD/skills/chrome-devtools-cli/references/installation.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Installation\n\nInstall the package globally to make the `chrome-devtools` command available. You only need to do this the first time you use it.\n\n```sh\nnpm i chrome-devtools-mcp@latest -g\nchrome-devtools status # check if install worked.\n```\n\n## Troubleshooting\n\n- **Command not found:** If `chrome-devtools` is not recognized, ensure your global npm `bin` directory is in your system's `PATH`. Restart your terminal or source your shell configuration file (e.g., `.bashrc`, `.zshrc`).\n- **Permission errors:** If you encounter `EACCES` or permission errors during installation, avoid using `sudo`. Instead, use a node version manager like `nvm`, or configure npm to use a different global directory.\n- **Old version running:** Run `chrome-devtools stop && npm uninstall -g chrome-devtools-mcp` before reinstalling, or ensure the latest version is being picked up by your path.\n","isInternal":false,"tokens":204,"sizeBytes":879},{"name":"SKILL.md","path":"skills/chrome-devtools-cli/SKILL.md","rawUrl":"https://raw.githubusercontent.com/ChromeDevTools/chrome-devtools-mcp/HEAD/skills/chrome-devtools-cli/SKILL.md","title":"chrome-devtools-cli","category":"anthropic-skill","format":"markdown","content":"---\nname: chrome-devtools-cli\ndescription: Use this skill to write shell scripts or run shell commands to automate tasks in the browser or otherwise use Chrome DevTools via CLI.\n---\n\nThe `chrome-devtools-mcp` CLI lets you interact with the browser from your terminal.\n\n## Setup\n\n_Note: If this is your very first time using the CLI, see [references/installation.md](references/installation.md) for setup. Installation is a one-time prerequisite and is **not** part of the regular AI workflow._\n\n## AI Workflow\n\n1. **Execute**: Run tools directly. If you don't know the target page's ID, run `chrome-devtools list_pages` to find it. The background server starts implicitly; **do not** run `start`/`status`/`stop` before each use.\n2. **Inspect**: Use `chrome-devtools take_snapshot <pageId>` to get an element `<uid>`.\n3. **Act**: Use `chrome-devtools click <pageId> <uid>`, `chrome-devtools fill <pageId> <uid> <value>`, etc. State persists across commands.\n\nSnapshot example:\n\n```\nuid=1_0 RootWebArea \"Example Domain\" url=\"https://example.com/\"\n  uid=1_1 heading \"Example Domain\" level=\"1\"\n```\n\n## Permissions & File Access\n\nBy default, the CLI has full filesystem access (`--allowUnrestrictedPaths=true`), allowing file-saving parameters (`--filePath`, `--outputDirPath`) and `upload_file` to access files anywhere on the system. Pass `--allowUnrestrictedPaths=false` if you want to restrict file access to the OS temp directory.\n\n## Command Usage\n\n```sh\nchrome-devtools <tool> [arguments] [flags]\n```\n\n- Required arguments are passed positionally; optional arguments use flags.\n- Use `--help` on any command for usage details.\n- Output defaults to plain Markdown-like text; pass `--output-format=json` for JSON.\n\n## Input Automation (<uid> from snapshot)\n\n```bash\nchrome-devtools take_snapshot 1 # Take a text snapshot of the page to get UIDs for elements\nchrome-devtools click 1 \"id\" # Clicks on the provided element\nchrome-devtools click 1 \"id\" --dblClick true --includeSnapshot true # Double clicks and returns a snapshot\nchrome-devtools drag 1 \"src\" \"dst\" # Drag an element onto another element\nchrome-devtools drag 1 \"src\" \"dst\" --includeSnapshot true # Drag an element and return a snapshot\nchrome-devtools fill 1 \"id\" \"text\" # Type text into an input, textarea, or select an option\nchrome-devtools fill 1 \"id\" \"text\" --includeSnapshot true # Fill an element and return a snapshot\nchrome-devtools handle_dialog 1 accept # Handle a browser dialog (accept/dismiss)\nchrome-devtools handle_dialog 1 dismiss --promptText \"hi\" # Dismiss a dialog with prompt text\nchrome-devtools hover 1 \"id\" # Hover over the provided element\nchrome-devtools hover 1 \"id\" --includeSnapshot true # Hover over an element and return a snapshot\nchrome-devtools press_key 1 \"Enter\" # Press a key or key combination (\"Control+A\", \"Escape\")\nchrome-devtools press_key 1 \"Control+A\" --includeSnapshot true # Press a key and return a snapshot\nchrome-devtools type_text 1 \"hello\" # Type text using keyboard into a focused input\nchrome-devtools type_text 1 \"hello\" --submitKey \"Enter\" # Type text and press a submit key\nchrome-devtools upload_file 1 \"id\" \"file.txt\" # Upload a file through a provided element\nchrome-devtools upload_file 1 \"id\" \"file.txt\" --includeSnapshot true # Upload a file and return a snapshot\n```\n\n## Navigation\n\n```bash\nchrome-devtools close_page 1 # Closes the page by its index\nchrome-devtools list_pages # Get a list of pages open in the browser\nchrome-devtools navigate_page 1 --url \"https://example.com\" # Navigates the currently selected page to a URL\nchrome-devtools navigate_page 1 --type \"reload\" --ignoreCache true # Reload page ignoring cache\nchrome-devtools navigate_page 1 --url \"https://example.com\" --timeout 5000 # Navigate with a timeout\nchrome-devtools navigate_page 1 --type \"reload\" --handleBeforeUnload \"accept\" # Handle before unload dialog\nchrome-devtools navigate_page 1 --type \"back\" --initScript \"foo()\" # Navigate back and run an init script\nchrome-devtools new_page \"https://example.com\" # Creates a new page\nchrome-devtools new_page \"https://example.com\" --background true --timeout 5000 # Create new page in background\nchrome-devtools new_page \"https://example.com\" --isolatedContext \"ctx\" # Create new page with isolated context\nchrome-devtools select_page 1 # Select a page as a context for future tool calls\nchrome-devtools select_page 1 --bringToFront true # Select a page and bring it to front\n```\n\n## Emulation\n\nEach `emulate` call replaces all previously configured emulation settings (except `--extraHttpHeaders`), so pass all desired settings in a single call.\n\n```bash\nchrome-devtools emulate 1 --networkConditions \"Offline\" # Emulate network conditions (resets other settings)\nchrome-devtools emulate 1 --cpuThrottlingRate 4 --geolocation \"0,0\" # Emulate CPU throttling and geolocation (resets other settings)\nchrome-devtools emulate 1 --colorScheme \"dark\" --viewport \"1920x1080\" # Emulate color scheme and viewport (resets other settings)\nchrome-devtools emulate 1 --userAgent \"Mozilla/5.0...\" # Emulate user agent (resets other settings)\nchrome-devtools resize_page 1 1920 1080 # Resizes the selected page's window\n```\n\n## Performance\n\n```bash\nchrome-devtools performance_analyze_insight 1 \"NAVIGATION_0\" \"LCPBreakdown\" # Get more details on a specific Performance Insight (pageId, insightSetId, insightName)\nchrome-devtools performance_start_trace 1 --reload true --autoStop false # Starts a performance trace recording (reload, autoStop)\nchrome-devtools performance_start_trace 1 --reload true --autoStop true --filePath \"t.json.gz\" # Start trace and save to a file\nchrome-devtools performance_stop_trace 1 # Stops the active performance trace\nchrome-devtools performance_stop_trace 1 --filePath \"t.json.gz\" # Stop trace and save to a file\n```\n\n## Memory\n\n```bash\nchrome-devtools take_heapsnapshot 1 \"./snap.heapsnapshot\" # Capture a memory heap snapshot\n```\n\n### Memory Debugging (requires `--memoryDebugging=true`)\n\n```bash\nchrome-devtools get_heapsnapshot_summary \"./snap.heapsnapshot\" # Get snapshot summary stats\nchrome-devtools compare_heapsnapshots \"./base.heapsnapshot\" \"./target.heapsnapshot\" # Compare two snapshots\nchrome-devtools get_heapsnapshot_details \"./snap.heapsnapshot\" # Snapshot statistics and per-class aggregates (with class IDs)\nchrome-devtools get_heapsnapshot_class_nodes \"./snap.heapsnapshot\" 18 # Inspect instances of a class (class ID from get_heapsnapshot_details)\nchrome-devtools get_heapsnapshot_dominators \"./snap.heapsnapshot\" 123 # Dominator tree for node\nchrome-devtools get_heapsnapshot_duplicate_strings \"./snap.heapsnapshot\" # Find duplicated strings\nchrome-devtools get_heapsnapshot_edges \"./snap.heapsnapshot\" 123 # Node edges/references\nchrome-devtools get_heapsnapshot_object_details \"./snap.heapsnapshot\" 123 # Object details by node ID\nchrome-devtools get_heapsnapshot_retainers \"./snap.heapsnapshot\" 123 # Retaining objects\nchrome-devtools get_heapsnapshot_retaining_paths \"./snap.heapsnapshot\" 123 # Shortest retaining paths\nchrome-devtools close_heapsnapshot \"./snap.heapsnapshot\" # Free memory from loaded snapshot\n```\n\n## Network\n\n```bash\nchrome-devtools get_network_request 1 # Get the currently selected network request for page 1\nchrome-devtools get_network_request 1 --reqid 1 --requestFilePath \"req.md\" # Get request by id and save to file\nchrome-devtools get_network_request 1 --reqid 1 --responseFilePath \"res.md\" # Save response body to file\nchrome-devtools list_network_requests 1 # List all network requests for page 1\nchrome-devtools list_network_requests 1 --pageSize 50 --pageIdx 0 # List network requests with pagination\nchrome-devtools list_network_requests 1 --resourceTypes fetch # Filter requests by resource type\nchrome-devtools list_network_requests 1 --includePreservedRequests true # Include preserved requests\n```\n\n## Debugging & Inspection\n\n```bash\nchrome-devtools evaluate_script \"() => document.title\" --pageId 1 # Evaluate a JavaScript function on page 1\nchrome-devtools evaluate_script \"(a) => a.innerText\" --pageId 1 --args 1_4 # Evaluate JS with UID arguments on page 1\nchrome-devtools evaluate_script --pageId 1 --sourcePath /path/to/script.js --format script # Evaluate a local classic JavaScript file on page 1\nchrome-devtools get_console_message 1 1 # Gets a console message by its ID\nchrome-devtools get_css_styles 1 \"1_4\" # Get CSS styles with pagination on page 1 (default: 10 rules, pageIdx 0)\nchrome-devtools get_css_styles 1 \"1_4\" --pageSize 20 --pageIdx 1 # Paginate CSS rules with custom page size and custom 0-based page index\nchrome-devtools lighthouse_audit 1 --mode \"navigation\" # Run Lighthouse audit for navigation\nchrome-devtools lighthouse_audit 1 --mode \"snapshot\" --device \"mobile\" # Run Lighthouse audit for a snapshot on mobile\nchrome-devtools lighthouse_audit 1 --outputDirPath ./out # Run Lighthouse audit and save reports\nchrome-devtools list_console_messages 1 # List all console messages\nchrome-devtools list_console_messages 1 --pageSize 20 --pageIdx 1 # List console messages with pagination\nchrome-devtools list_console_messages 1 --types error --types info # Filter console messages by type\nchrome-devtools list_console_messages 1 --includePreservedMessages true # Include preserved messages\nchrome-devtools take_screenshot 1 # Take a screenshot of the page viewport\nchrome-devtools take_screenshot 1 --fullPage true --format \"jpeg\" --quality 80 # Take a full page screenshot as JPEG with quality\nchrome-devtools take_screenshot 1 --uid \"id\" --filePath \"s.png\" # Take a screenshot of an element\nchrome-devtools take_snapshot 1 # Take a text snapshot of the page from the a11y tree\nchrome-devtools take_snapshot 1 --verbose true --filePath \"s.txt\" # Take a verbose snapshot and save to file\nchrome-devtools screencast_start 1 --filePath \"screen.mp4\" # Starts an mp4 screencast recording on page 1\nchrome-devtools screencast_stop 1 # Stops the active screencast on page 1\n```\n\n## Extensions\n\n```bash\nchrome-devtools list_extensions # Lists all the Chrome extensions installed in the browser\nchrome-devtools install_extension \"/path/to/extension\" # Installs a Chrome extension from the given path\nchrome-devtools uninstall_extension \"extension_id\" # Uninstalls a Chrome extension by its ID\nchrome-devtools reload_extension \"extension_id\" # Reloads an unpacked Chrome extension by its ID\nchrome-devtools trigger_extension_action \"extension_id\" # Triggers the default action of an extension by its ID\n```\n\n## Progressive Web Apps (requires `--categoryPwa=true`)\n\n```bash\nchrome-devtools install_pwa \"https://example.com/\" \"https://example.com/\" # Install PWA by manifest ID and install URL\nchrome-devtools launch_pwa \"https://example.com/\" # Launch installed PWA\nchrome-devtools get_os_app_state \"https://example.com/\" # Get OS app installation state\nchrome-devtools uninstall_pwa \"https://example.com/\" # Uninstall PWA and close windows\n```\n\n## Experimental Features\n\nExperimental tools are disabled by default. Enable them with the corresponding flag during `start`.\n\n```bash\nchrome-devtools click_at 1 100 200 # Clicks at the provided coordinates on page 1 (requires --experimentalVision=true)\nchrome-devtools list_webmcp_tools 1 # List all WebMCP tools on page 1 (requires --categoryExperimentalWebmcp=true)\nchrome-devtools execute_webmcp_tool 1 \"tool_name\" --input '{\"arg\":\"val\"}' # Execute a WebMCP tool on page 1 (requires --categoryExperimentalWebmcp=true)\nchrome-devtools list_3p_developer_tools 1 # List third-party developer tools on page 1 (requires --categoryExperimentalThirdParty=true)\nchrome-devtools execute_3p_developer_tool 1 \"tool_name\" --params '{\"arg\":\"val\"}' # Execute third-party developer tool on page 1 (requires --categoryExperimentalThirdParty=true)\n```\n\n## Service Management\n\n```bash\nchrome-devtools start   # Start or restart chrome-devtools-mcp\nchrome-devtools start --headless=false # Start with visible browser window\nchrome-devtools status  # Checks if chrome-devtools-mcp is running\nchrome-devtools stop    # Stop chrome-devtools-mcp if any\n```\n","frontmatter":{"name":"chrome-devtools-cli","description":"Use this skill to write shell scripts or run shell commands to automate tasks in the browser or otherwise use Chrome DevTools via CLI."},"isInternal":false,"tokens":2905,"sizeBytes":12031},{"name":"SKILL.md","path":"skills/chrome-devtools/SKILL.md","rawUrl":"https://raw.githubusercontent.com/ChromeDevTools/chrome-devtools-mcp/HEAD/skills/chrome-devtools/SKILL.md","title":"chrome-devtools","category":"anthropic-skill","format":"markdown","content":"---\nname: chrome-devtools\ndescription: Uses Chrome DevTools via MCP for efficient debugging, troubleshooting and browser automation. Use when debugging web pages, automating browser interactions, analyzing performance, or inspecting network requests. This skill does not apply to `--slim` mode (MCP configuration).\n---\n\n## Core Concepts\n\n**Browser lifecycle**: Browser starts automatically on first tool call using a persistent Chrome profile. Configure via CLI args in the MCP server configuration: `npx chrome-devtools-mcp@latest --help`.\nAddional tooling can be enabled by providing the following flags:\n\n- For extension tooling, use the `--categoryExtensions` flag.\n- For memory tooling, use the `--memoryDebugging` flag.\n\n**Page targeting**: Page-scoped tools require a `pageId` parameter to target a specific page. Use `list_pages` to see available pages and their IDs (e.g. `pageId: 1`), or use the ID returned when creating a page with `new_page`.\nNote: For `evaluate_script`, `pageId` is required when targeting pages. However, when `--categoryExtensions` is enabled, `pageId` is optional so you can pass `serviceWorkerId` instead to evaluate inside an extension background service worker.\n**Element interaction**: Use `take_snapshot` to get page structure with element `uid`s. Each element has a unique `uid` for interaction. If an element isn't found, take a fresh snapshot - the element may have been removed or the page changed.\n\n## Workflow Patterns\n\n### Before interacting with a page\n\n1. Navigate: `navigate_page` or `new_page`\n2. Wait: `wait_for` to ensure content is loaded if you know what you look for.\n3. Snapshot: `take_snapshot` with `pageId` to understand page structure\n4. Interact: Use element `uid`s from snapshot for `click`, `fill`, etc., passing the corresponding `pageId`.\n\n### Efficient data retrieval\n\n- Use `filePath` parameter for large outputs (screenshots, snapshots, traces)\n- Use pagination (`pageIdx`, `pageSize`) and filtering (`types`) to minimize data\n- Set `includeSnapshot: false` on input actions unless you need updated page state\n\n### Tool selection\n\n- **Automation/interaction**: `take_snapshot` (text-based, faster, better for automation)\n- **Visual inspection**: `take_screenshot` (when user needs to see visual state)\n- **CSS & Styling inspection**: Use `get_css_styles` to inspect matched rules, cascade, and CSS variables. Treat the output as authoritative and complete.\n- **Additional details**: Use `evaluate_script` for runtime DOM/JS data not available via `get_css_styles` or `take_snapshot`.\n\n### Parallel execution\n\nYou can send multiple tool calls in parallel, but maintain correct order: navigate → wait → snapshot → interact.\n\n### Testing an extension\n\n> **Before proceeding**: Extension tools (`install_extension`, `list_extensions`, etc.) are only available when the MCP server is started with the `--categoryExtensions` flag. If these tools are not in your tool list, stop and ask the user to update their MCP server configuration:\n>\n> ```json\n> {\n>   \"mcpServers\": {\n>     \"chrome-devtools\": {\n>       \"command\": \"npx\",\n>       \"args\": [\"chrome-devtools-mcp@latest\", \"--categoryExtensions\"]\n>     }\n>   }\n> }\n> ```\n>\n> After updating, the user must restart the MCP server (or their AI client) for the change to take effect.\n\n1. **Install**: Use `install_extension` with the path to the unpacked extension.\n2. **Identify**: Get the extension ID from the response or by calling `list_extensions`.\n3. **Trigger Action**: Use `trigger_extension_action` to open the popup or side panel if applicable.\n4. **Verify Service Worker**: Use `evaluate_script` with `serviceWorkerId` (omitting `pageId` and `args`) to check extension state or trigger background actions. When evaluating in a page, pass `pageId` (omitting `serviceWorkerId`).\n5. **Verify Page Behavior**: Navigate to a page where the extension operates and use `take_snapshot` to check if content scripts injected elements or modified the page correctly.\n\n## Troubleshooting\n\nIf `chrome-devtools-mcp` is insufficient, guide users to use Chrome DevTools UI:\n\n- https://developer.chrome.com/docs/devtools\n- https://developer.chrome.com/docs/devtools/ai-assistance\n\nIf there are errors launching `chrome-devtools-mcp` or Chrome, refer to https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/troubleshooting.md.\n","frontmatter":{"name":"chrome-devtools","description":"Uses Chrome DevTools via MCP for efficient debugging, troubleshooting and browser automation. Use when debugging web pages, automating browser interactions, analyzing performance, or inspecting network requests. This skill does not apply to `--slim` mode (MCP configuration)."},"isInternal":false,"tokens":984,"sizeBytes":4351},{"name":"SKILL.md","path":"skills/cookie-debugging/SKILL.md","rawUrl":"https://raw.githubusercontent.com/ChromeDevTools/chrome-devtools-mcp/HEAD/skills/cookie-debugging/SKILL.md","title":"cookie-debugging","category":"anthropic-skill","format":"markdown","content":"---\nname: cookie-debugging\ndescription: Uses Chrome DevTools MCP for inspecting, debugging, and testing cookies, session state, authentication issues, and cookie consent compliance. Use when diagnosing 401/403 errors, authentication redirects, session expiration, Cookie/Set-Cookie header issues, cookie banner consent conformance, or third-party cookie/SameSite/Partitioned cookie warnings.\n---\n\n## Core Concepts\n\n**Page Targeting**: Page-scoped tools (`list_network_requests`, `get_network_request`, `list_console_messages`, `evaluate_script`, `navigate_page`, `take_snapshot`, `click`, `lighthouse_audit`, `close_page`, etc.) require a `pageId` parameter. Retrieve available page IDs using `list_pages` or from `new_page`.\n\n### HttpOnly vs Client-Side Storage\n\nCookies marked `HttpOnly` cannot be accessed or modified by client-side JavaScript (`cookieStore` or `document.cookie`). However, the browser **automatically attaches active HttpOnly cookies to outgoing HTTP request headers (`Cookie`)**.\n\n- To inspect current `HttpOnly` values: Look at the `Cookie` request header of any outgoing HTTP request via `get_network_request`.\n- To inspect how cookies were created or configured: Look at the `Set-Cookie` response header of login/auth responses.\n- To inspect non-`HttpOnly` cookies: Use `evaluate_script` with the modern `cookieStore` API (`async () => await cookieStore.getAll()`).\n\n### Session Strategy: Live Tab vs Isolated Context\n\nChoose the right session environment to avoid state contamination (e.g., residual analytics or auth tokens):\n\n| Strategy                            | When to Use                                                                   | Setup / Teardown                                                                                              |\n| :---------------------------------- | :---------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------ |\n| **Live Tab (Active Page)**          | Diagnosing an active user session, live 401/403 error, or current state.      | Operates on the user's existing page; get its `pageId` from `list_pages`.                                     |\n| **Clean-Slate (`isolatedContext`)** | Testing cookie consent banners, first-time visits, or zero-cookie guarantees. | Call `new_page` with a unique `isolatedContext` (e.g. `\"consent-audit-1\"`). When finished, call `close_page`. |\n\n### Client-Side Capabilities & Limitations\n\n| Action                                                           | Client JavaScript (`cookieStore` / `document.cookie`) | DevTools Network & Context Tools                        |\n| :--------------------------------------------------------------- | :---------------------------------------------------- | :------------------------------------------------------ |\n| **Read Non-HttpOnly**                                            | ✅ `async () => await cookieStore.getAll()`           | ✅ `get_network_request` (Request `Cookie`)             |\n| **Read HttpOnly**                                                | ❌ Blocked by browser security                        | ✅ `get_network_request` (Request `Cookie`)             |\n| **Inspect Attributes** (`Domain`, `Path`, `SameSite`, `Expires`) | ✅ `async () => await cookieStore.getAll()`           | ✅ `get_network_request` (Response `Set-Cookie`)        |\n| **Modify / Delete Non-HttpOnly**                                 | ✅ `async () => await cookieStore.set(...)`           | N/A                                                     |\n| **Modify / Delete HttpOnly**                                     | ❌ **Silent failure** in JavaScript                   | ✅ Use `new_page(isolatedContext: ...)` for clean state |\n\n> [!WARNING]\n> Attempting to clear an `HttpOnly` cookie via JavaScript (`cookieStore.delete` or `document.cookie = \"...; max-age=0\"`) will silently fail. To test in an unauthenticated or fresh state, always spawn a new isolated context using `new_page` with `isolatedContext`.\n\n---\n\n## Workflow Patterns\n\n### 1. Diagnosing Authentication Failures & Redirects (401 / 403)\n\nWhen an authenticated page request fails, returns 401/403, or redirects to login:\n\n1. **List Recent Requests**: Call `list_network_requests` with `includePreservedRequests: true`.\n2. **Find the Target Request**: Locate the failing request (401/403) or redirect (302/307).\n3. **Inspect Outgoing `Cookie` Header**: Call `get_network_request` with the `reqid`.\n   - Verify if the `Cookie` header was attached and whether required tokens (e.g. `SESSION_ID`, `auth_token`) were sent.\n4. **Trigger Active Inspection (If no recent request exists)**:\n   - If the cookie was set in a previous session and no network call is listed, trigger a request:\n     - Use `navigate_page` with `type: \"reload\"`, OR\n     - Call `evaluate_script` with `() => fetch(window.location.href)`\n   - Then call `get_network_request` on the new request to inspect the active `Cookie` header.\n5. **Trace the Setting Request**: If the cookie is missing or rejected:\n   - Check earlier login/handshake responses for `Set-Cookie` directives:\n     - **Path mismatch**: e.g., `Path=/api` when the request is to `/`.\n     - **Domain mismatch**: e.g., `Domain=api.example.com` preventing cookies on `sub.example.com`.\n     - **Secure flag on HTTP**: `Secure` cookies are never sent over unencrypted `http://`.\n     - **SameSite blocking**: `SameSite=Strict` cookies are omitted on cross-site navigations.\n     - **Expiration**: Check if `Expires` or `Max-Age` elapsed.\n\n### 2. Cookie Banner & Consent Conformance Testing\n\nTo verify that no non-essential or tracking cookies are set before consent or when declining:\n\n1. **Start Clean**: Open a fresh isolated context with a dedicated name:\n   ```json\n   {\"url\": \"<PAGE_URL>\", \"isolatedContext\": \"consent-test-1\"}\n   ```\n2. **Record Baseline Cookies**: Before interacting with the banner, run `evaluate_script` with `async () => await cookieStore.getAll()`.\n3. **Inspect Premature Network Requests & Issues**:\n   - Call `list_network_requests` to ensure no third-party tracking beacons fired before consent.\n   - Call `list_console_messages` with `types: [\"issue\"]` to check for tracking warnings.\n4. **Interact with Consent Banner**:\n   - Capture snapshot with `take_snapshot` to locate the \"Decline\" or \"Reject All\" button `uid`.\n   - Click the button with `click`.\n5. **Verify Cookie Difference**:\n   - Run `evaluate_script` with `async () => await cookieStore.getAll()` after clicking to assert that only strictly necessary or consent-state cookies exist.\n6. **Test Consent Revocation (Lifecycle Audit)**:\n   - When auditing consent withdrawal or preference changes:\n     - Locate and click the \"Cookie Settings\", \"Manage Preferences\", or footer privacy trigger (`take_snapshot` $\\rightarrow$ `click`).\n     - Deselect non-essential categories or click \"Revoke All\" / \"Save Preferences\".\n     - Re-query `cookieStore.getAll()` to verify previously accepted non-essential cookies were cleared or expired.\n     - Call `list_network_requests` on subsequent actions to ensure tracking beacons are no longer fired.\n7. **Teardown Context**: Call `close_page` when the audit is complete to prevent leftover cookies from affecting subsequent tasks.\n\n### 3. Auditing Cookie Security, SameSite & CHIPS (Partitioned Cookies)\n\n1. **Fast-Track: Native DevTools Issues (Recommended)**:\n   - Call `list_console_messages` with:\n     ```json\n     {\n       \"pageId\": 1,\n       \"types\": [\"issue\"],\n       \"includePreservedMessages\": true\n     }\n     ```\n   - Check for `CookieIssue` entries, such as:\n     - `SameSiteNoneInsecure`: `SameSite=None` without `Secure`.\n     - `ThirdPartyCookiePhaseout`: Third-party cookie blocked or restricted.\n     - `SchemefulSameSite`: Cross-scheme cookie issues.\n     - `PartitionedCookies`: Invalid CHIPS partitioning attributes.\n2. **Deep Audit: Lighthouse Third-Party Cookies**:\n   - Run `lighthouse_audit` with `mode: \"navigation\"` (omit `outputDirPath` to use the default temporary files, or specify a directory within the workspace).\n   - **Extract the specific cookie audit** from the returned `report.json` path without loading the full report into context:\n     ```bash\n     node -e \"const r=require('<path/to/report.json>'); const a=r.audits['third-party-cookies']; console.log(JSON.stringify({score: a?.score, displayValue: a?.displayValue, items: a?.details?.items}))\"\n     ```\n\n### 4. Client-Side Cookie Inspection & Manipulation\n\nFor client-accessible, non-`HttpOnly` cookies (e.g., UI preferences, non-sensitive feature flags):\n\n1. **Read Cookies & Attributes**:\n   - Use the modern asynchronous Cookie Store API:\n     ```js\n     async () => await cookieStore.getAll();\n     ```\n   - _Fallback for insecure HTTP origins_: `() => document.cookie`.\n2. **Set / Modify Cookie**:\n   - Set client cookie via `cookieStore`:\n     ```js\n     async () =>\n       await cookieStore.set({\n         name: 'theme',\n         value: 'dark',\n         expires: Date.now() + 86400000,\n         sameSite: 'lax',\n       });\n     ```\n3. **Delete Cookie**:\n   - Clear client cookie:\n     ```js\n     async () => await cookieStore.delete('theme');\n     ```\n\n---\n\n## Troubleshooting\n\n- **`cookieStore` is undefined**: `cookieStore` requires a Secure Context (`https://`, `localhost`, or `127.0.0.1`). On non-secure HTTP origins, use `() => document.cookie` or test over HTTPS.\n- **`evaluate_script` returns empty / unresolved Promise**: `cookieStore` methods are asynchronous. Always wrap calls with `async () => await cookieStore.getAll()`.\n- **Cookie not visible in JavaScript**: The cookie is marked `HttpOnly`. Trigger a network request and call `get_network_request` to view it in the `Cookie` request header.\n- **JavaScript deletion did not remove cookie**: The cookie is `HttpOnly` or requires matching `Path` and `Domain` parameters. Use a fresh `isolatedContext` with `new_page` for a clean slate.\n- **Cookie set in response but not sent in requests**:\n  - Verify if page is `http://` while cookie specifies `Secure`.\n  - Check if `Domain` restricts subdomains.\n  - Check `list_console_messages(types: [\"issue\"])` for browser rejection reasons.\n- **Residual cookies contaminating audits**: Always use `new_page` with a unique `isolatedContext` when running compliance tests, and call `close_page` when done.\n","frontmatter":{"name":"cookie-debugging","description":"Uses Chrome DevTools MCP for inspecting, debugging, and testing cookies, session state, authentication issues, and cookie consent compliance. Use when diagnosing 401/403 errors, authentication redirects, session expiration, Cookie/Set-Cookie header issues, cookie banner consent conformance, or third-party cookie/SameSite/Partitioned cookie warnings."},"isInternal":false,"tokens":2269,"sizeBytes":10426},{"name":"elements-and-size.md","path":"skills/debug-optimize-lcp/references/elements-and-size.md","rawUrl":"https://raw.githubusercontent.com/ChromeDevTools/chrome-devtools-mcp/HEAD/skills/debug-optimize-lcp/references/elements-and-size.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Elements and Size for LCP\n\n## What Elements are Considered?\n\nThe types of elements considered for Largest Contentful Paint (LCP) are:\n\n- **`<img>` elements**: The first frame presentation time is used for animated content like GIFs.\n- **`<image>` elements** inside an `<svg>` element.\n- **`<video>` elements**: The poster image load time or first frame presentation time, whichever is earlier.\n- **Background images**: Elements with a background image loaded using `url()`.\n- **Block-level elements**: Containing text nodes or other inline-level text element children.\n\n## Heuristics to Exclude Non-Contentful Elements\n\nChromium-based browsers use heuristics to exclude:\n\n- Elements with **opacity of 0**.\n- Elements that **cover the full viewport** (likely background).\n- **Placeholder images** or low-entropy images.\n\n## How is an Element's Size Determined?\n\n- **Visible Area**: Typically the size visible within the viewport. Extending outside, clipped, or overflow portions don't count.\n- **Image Elements**: Either the visible size or the intrinsic size, whichever is smaller.\n- **Text Elements**: The smallest rectangle containing all text nodes.\n- **Exclusions**: Margin, padding, and borders are not considered toward the size.\n- **Containment**: Every text node belongs to its closest block-level ancestor element.\n","isInternal":false,"tokens":284,"sizeBytes":1326},{"name":"lcp-breakdown.md","path":"skills/debug-optimize-lcp/references/lcp-breakdown.md","rawUrl":"https://raw.githubusercontent.com/ChromeDevTools/chrome-devtools-mcp/HEAD/skills/debug-optimize-lcp/references/lcp-breakdown.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Largest Contentful Paint (LCP) Breakdown\n\nLCP measures the time from when the user initiates loading the page until the largest image or text block is rendered within the viewport. To provide a good user experience, sites should strive to have an LCP of 2.5 seconds or less for at least 75% of page visits.\n\n## The Four Subparts of LCP\n\nEvery page's LCP consists of these four subcategories. There's no gap or overlap between them, and they add up to the full LCP time.\n\n| LCP subpart                   | % of LCP (Optimal) | Description                                                                                                                                                              |\n| ----------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| **Time to First Byte (TTFB)** | ~40%               | The time from when the user initiates loading the page until the browser receives the first byte of the HTML document response.                                          |\n| **Resource load delay**       | <10%               | The time between TTFB and when the browser starts loading the LCP resource. If the LCP element doesn't require a resource load (e.g., system font text), this time is 0. |\n| **Resource load duration**    | ~40%               | The duration of time it takes to load the LCP resource itself. If the LCP element doesn't require a resource load, this time is 0.                                       |\n| **Element render delay**      | <10%               | The time between when the LCP resource finishes loading and the LCP element rendering fully.                                                                             |\n\n## Why the Breakdown Matters\n\nOptimizing for LCP requires identifying which of these subparts is the bottleneck:\n\n- **Large delta between TTFB and FCP**: Indicates the browser needs to download a lot of render-blocking assets or complete a lot of work (e.g., client-side rendering).\n- **Large delta between FCP and LCP**: Indicates the LCP resource is not immediately available for the browser to prioritize or the browser is completing other work before it can display the LCP content.\n- **Large resource load delay**: Indicates the resource is not discoverable early or is deprioritized.\n- **Large element render delay**: Indicates rendering is blocked by stylesheets, scripts, or long tasks.\n","isInternal":false,"tokens":468,"sizeBytes":2525},{"name":"lcp-snippets.md","path":"skills/debug-optimize-lcp/references/lcp-snippets.md","rawUrl":"https://raw.githubusercontent.com/ChromeDevTools/chrome-devtools-mcp/HEAD/skills/debug-optimize-lcp/references/lcp-snippets.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# LCP Debugging Snippets\n\nUse these JavaScript snippets with the `evaluate_script` tool to extract deep insights from the page.\n\n## 1. Identify LCP Element\n\nUse this snippet to identify the LCP element and get raw timing data from the Performance API.\n\n```javascript\nasync () => {\n  return await new Promise(resolve => {\n    new PerformanceObserver(list => {\n      const entries = list.getEntries();\n      const last = entries[entries.length - 1];\n      resolve({\n        element: last.element?.tagName,\n        id: last.element?.id,\n        className: last.element?.className,\n        url: last.url,\n        startTime: last.startTime,\n        renderTime: last.renderTime,\n        loadTime: last.loadTime,\n        size: last.size,\n      });\n    }).observe({type: 'largest-contentful-paint', buffered: true});\n  });\n};\n```\n\n## 2. Audit Common Issues\n\nUse this snippet to check for common DOM-based LCP issues (lazy loading, priority).\n\n```javascript\n() => {\n  const issues = [];\n\n  // Check for lazy-loaded images in viewport\n  document.querySelectorAll('img[loading=\"lazy\"]').forEach(img => {\n    const rect = img.getBoundingClientRect();\n    if (rect.top < window.innerHeight) {\n      issues.push({\n        issue: 'lazy-loaded image in viewport',\n        element: img.outerHTML.substring(0, 200),\n        fix: 'Remove loading=\"lazy\" from this image — it is in the initial viewport and may be the LCP element',\n      });\n    }\n  });\n\n  // Check for LCP-candidate images missing fetchpriority\n  document.querySelectorAll('img:not([fetchpriority])').forEach(img => {\n    const rect = img.getBoundingClientRect();\n    if (rect.top < window.innerHeight && rect.width * rect.height > 50000) {\n      issues.push({\n        issue: 'large viewport image without fetchpriority',\n        element: img.outerHTML.substring(0, 200),\n        fix: 'Add fetchpriority=\"high\" to this image — it is large and visible in the initial viewport',\n      });\n    }\n  });\n\n  // Check for render-blocking scripts in head\n  document\n    .querySelectorAll(\n      'head script:not([async]):not([defer]):not([type=\"module\"])',\n    )\n    .forEach(script => {\n      if (script.src) {\n        issues.push({\n          issue: 'render-blocking script in head',\n          element: script.outerHTML.substring(0, 200),\n          fix: 'Add async or defer attribute, or move to end of body',\n        });\n      }\n    });\n\n  return {issueCount: issues.length, issues};\n};\n```\n","isInternal":false,"tokens":550,"sizeBytes":2436},{"name":"optimization-strategies.md","path":"skills/debug-optimize-lcp/references/optimization-strategies.md","rawUrl":"https://raw.githubusercontent.com/ChromeDevTools/chrome-devtools-mcp/HEAD/skills/debug-optimize-lcp/references/optimization-strategies.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# LCP Optimization Strategies\n\n## 1. Eliminate Resource Load Delay\n\n**Goal**: Ensure the LCP resource starts loading as early as possible.\n\n- **Early Discovery**: Ensure the LCP resource is discoverable in the initial HTML document response (not dynamically added by JS or hidden in `data-src`).\n- **Preload**: Use `<link rel=\"preload\">` with `fetchpriority=\"high\"` for critical images or fonts.\n- **Avoid Lazy Loading**: Never set `loading=\"lazy\"` on the LCP image.\n- **Fetch Priority**: Use `fetchpriority=\"high\"` on the `<img>` tag.\n- **Same Origin**: Host critical resources on the same origin or use `<link rel=\"preconnect\">`.\n\n## 2. Eliminate Element Render Delay\n\n**Goal**: Ensure the LCP element can render immediately after its resource has finished loading.\n\n- **Minimize Render-Blocking CSS**: Inline critical CSS and defer non-critical CSS. Ensure the stylesheet is smaller than the LCP resource.\n- **Minimize Render-Blocking JS**: Avoid synchronous scripts in the `<head>`. Inline very small scripts.\n- **Server-Side Rendering (SSR)**: Deliver the full HTML markup from the server so image resources are discoverable immediately.\n- **Break Up Long Tasks**: Prevent large JavaScript tasks from blocking the main thread during rendering.\n\n## 3. Reduce Resource Load Duration\n\n**Goal**: Reduce the time spent transferring the bytes of the resource.\n\n- **Optimize Resource Size**: Serve optimal image sizes, use modern formats (AVIF, WebP), and compress images/fonts.\n- **Geographic Proximity (CDN)**: Use a Content Delivery Network to get servers closer to users.\n- **Reduce Contention**: Use `fetchpriority=\"high\"` to prevent lower-priority resources from competing for bandwidth.\n- **Caching**: Use efficient `Cache-Control` policies.\n\n## 4. Reduce Time to First Byte (TTFB)\n\n**Goal**: Deliver the initial HTML as quickly as possible.\n\n- **Minimize Redirects**: Avoid multiple redirects from advertisements or shortened links.\n- **CDN Caching**: Cache static HTML documents at the edge.\n- **Edge Computing**: Move dynamic logic to the edge to avoid trips to the origin server.\n- **Back/Forward Cache**: Ensure pages are eligible for bfcache.\n","isInternal":false,"tokens":479,"sizeBytes":2154},{"name":"SKILL.md","path":"skills/debug-optimize-lcp/SKILL.md","rawUrl":"https://raw.githubusercontent.com/ChromeDevTools/chrome-devtools-mcp/HEAD/skills/debug-optimize-lcp/SKILL.md","title":"debug-optimize-lcp","category":"anthropic-skill","format":"markdown","content":"---\nname: debug-optimize-lcp\ndescription: Guides debugging and optimizing Largest Contentful Paint (LCP) using Chrome DevTools MCP tools. Use this skill whenever the user asks about LCP performance, slow page loads, Core Web Vitals optimization, or wants to understand why their page's main content takes too long to appear. Also use when the user mentions \"largest contentful paint\", \"page load speed\", \"CWV\", or wants to improve how fast their hero image or main content renders.\n---\n\n## What is LCP and why it matters\n\nLargest Contentful Paint (LCP) measures how quickly a page's main content becomes visible. It's the time from navigation start until the largest image or text block renders in the viewport.\n\n- **Good**: 2.5 seconds or less\n- **Needs improvement**: 2.5–4.0 seconds\n- **Poor**: greater than 4.0 seconds\n\nLCP is a Core Web Vital that directly affects user experience and search ranking. On 73% of mobile pages, the LCP element is an image.\n\n## LCP Subparts Breakdown\n\nEvery page's LCP breaks down into four sequential subparts with no gaps or overlaps. Understanding which subpart is the bottleneck is the key to effective optimization.\n\n| Subpart                       | Ideal % of LCP | What it measures                               |\n| ----------------------------- | -------------- | ---------------------------------------------- |\n| **Time to First Byte (TTFB)** | ~40%           | Navigation start → first byte of HTML received |\n| **Resource load delay**       | <10%           | TTFB → browser starts loading the LCP resource |\n| **Resource load duration**    | ~40%           | Time to download the LCP resource              |\n| **Element render delay**      | <10%           | LCP resource downloaded → LCP element rendered |\n\nThe \"delay\" subparts should be as close to zero as possible. If either delay subpart is large relative to the total LCP, that's the first place to optimize.\n\n**Common Pitfall**: Optimizing one subpart (like compressing an image to reduce load duration) without checking others. If render delay is the real bottleneck, a smaller image won't help — the saved time just shifts to render delay.\n\n## Debugging Workflow\n\nFollow these steps in order. Each step builds on the previous one.\n\n### Step 1: Record a Performance Trace\n\nNavigate to the page, then record a trace with reload to capture the full page load including LCP:\n\n1. `navigate_page` with `pageId` to the target URL.\n2. `performance_start_trace` with `pageId`, `reload: true` and `autoStop: true`.\n\nThe trace results will include LCP timing and available insight sets. Note the insight set IDs from the output — you'll need them in the next step.\n\n### Step 2: Analyze LCP Insights\n\nUse `performance_analyze_insight` to drill into LCP-specific insights. Look for these insight names in the trace results:\n\n- **LCPBreakdown** — Shows the four LCP subparts with timing for each.\n- **DocumentLatency** — Server response time issues affecting TTFB.\n- **RenderBlocking** — Resources blocking the LCP element from rendering.\n- **LCPDiscovery** — Whether the LCP resource was discoverable early.\n\nCall `performance_analyze_insight` with `pageId`, the insight set ID, and the insight name from the trace results.\n\n### Step 3: Identify the LCP Element\n\nUse `evaluate_script` (with `pageId`) and the **\"Identify LCP Element\" snippet** found in [references/lcp-snippets.md](references/lcp-snippets.md) to reveal the LCP element's tag, resource URL, and raw timing data.\n\nThe `url` field tells you what resource to look for in the network waterfall. If `url` is empty, the LCP element is text-based (no resource to load).\n\n### Step 4: Check the Network Waterfall\n\nUse `list_network_requests` to see when the LCP resource loaded relative to other resources:\n\n- Call `list_network_requests` with `pageId` filtered by `resourceTypes: [\"image\", \"font\"]` (adjust based on Step 3).\n- Then use `get_network_request` with `pageId` and the LCP resource's request ID for full details.\n\n**Key Checks:**\n\n- **Start Time**: Compare against the HTML document and the first resource. If the LCP resource starts much later than the first resource, there's resource load delay to eliminate.\n- **Duration**: A large resource load duration suggests the file is too big or the server is slow.\n\n### Step 5: Inspect HTML for Common Issues\n\nUse `evaluate_script` (with `pageId`) and the **\"Audit Common Issues\" snippet** found in [references/lcp-snippets.md](references/lcp-snippets.md) to check for lazy-loaded images in the viewport, missing fetchpriority, and render-blocking scripts.\n\n## Optimization Strategies\n\nAfter identifying the bottleneck subpart, apply these prioritized fixes.\n\n### 1. Eliminate Resource Load Delay (target: <10%)\n\nThe most common bottleneck. The LCP resource should start loading immediately.\n\n- **Root Cause**: LCP image loaded via JS/CSS, `data-src` usage, or `loading=\"lazy\"`.\n- **Fix**: Use standard `<img>` with `src`. **Never** lazy-load the LCP image.\n- **Fix**: Add `<link rel=\"preload\" fetchpriority=\"high\">` if the image isn't discoverable in HTML.\n- **Fix**: Add `fetchpriority=\"high\"` to the LCP `<img>` tag.\n\n### 2. Eliminate Element Render Delay (target: <10%)\n\nThe element should render immediately after loading.\n\n- **Root Cause**: Large stylesheets, synchronous scripts in `<head>`, or main thread blocking.\n- **Fix**: Inline critical CSS, defer non-critical CSS/JS.\n- **Fix**: Break up long tasks blocking the main thread.\n- **Fix**: Use Server-Side Rendering (SSR) so the element exists in initial HTML.\n\n### 3. Reduce Resource Load Duration (target: ~40%)\n\nMake the resource smaller or faster to deliver.\n\n- **Fix**: Use modern formats (WebP, AVIF) and responsive images (`srcset`).\n- **Fix**: Serve from a CDN.\n- **Fix**: Set `Cache-Control` headers.\n- **Fix**: Use `font-display: swap` if LCP is text blocked by a web font.\n\n### 4. Reduce TTFB (target: ~40%)\n\nThe HTML document itself takes too long to arrive.\n\n- **Fix**: Minimize redirects and optimize server response time.\n- **Fix**: Cache HTML at the edge (CDN).\n- **Fix**: Ensure pages are eligible for back/forward cache (bfcache).\n\n## Verifying Fixes & Emulation\n\n- **Verification**: Re-run the trace (`performance_start_trace` with `pageId` and `reload: true`) and compare the new subpart breakdown. The bottleneck should shrink.\n- **Emulation**: Lab measurements differ from real-world experience. Use `emulate` to test under constraints:\n  - `emulate` with `pageId`, `networkConditions: \"Fast 3G\"` and `cpuThrottlingRate: 4`.\n  - This surfaces issues visible only on slower connections/devices.\n","frontmatter":{"name":"debug-optimize-lcp","description":"Guides debugging and optimizing Largest Contentful Paint (LCP) using Chrome DevTools MCP tools. Use this skill whenever the user asks about LCP performance, slow page loads, Core Web Vitals optimization, or wants to understand why their page's main content takes too long to appear. Also use when the user mentions \"largest contentful paint\", \"page load speed\", \"CWV\", or wants to improve how fast their hero image or main content renders."},"isInternal":false,"tokens":1554,"sizeBytes":6608},{"name":"common-leaks.md","path":"skills/memory-leak-debugging/references/common-leaks.md","rawUrl":"https://raw.githubusercontent.com/ChromeDevTools/chrome-devtools-mcp/HEAD/skills/memory-leak-debugging/references/common-leaks.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Common Memory Leaks\n\nWhen analyzing retaining paths, dominator chains, or class diffs with Chrome DevTools MCP memory tools, look for these common patterns in the codebase:\n\n## 1. Uncleared Event Listeners\n\nEvent listeners attached to global objects (like `window` or `document`) or long-living objects prevent garbage collection of the objects referenced in their callbacks.\n\n**Fix:** Always call `removeEventListener` when a component unmounts or the listener is no longer needed.\n\n## 2. Detached DOM Nodes\n\nA DOM node is removed from the document tree but is still referenced by a JavaScript variable. While detachedness is a good signal for a memory leak, it's not always a bug. For example, websites sometimes intentionally cache detached navigation trees.\n\n**Fix:** Signal the detached nodes to the user first. **Ask the user first** before nulling the references or changing the code, as the detached nodes might be part of an intentional cache. If confirmed as a leak, ensure variables holding DOM references are set to `null` when the node is removed, or limit their scope.\n\n## 3. Unintentional Global Variables\n\nVariables declared without `var`, `let`, or `const` (in non-strict mode) or explicitly attached to `window` remain in memory forever.\n\n**Fix:** Use strict mode, properly declare variables, and avoid global state.\n\n## 4. Closures\n\nClosures can unintentionally keep references to large objects in their outer scope.\n\n**Fix:** Nullify large objects when they are no longer needed, or refactor the closure to not capture unnecessary variables.\n\n## 5. Unbounded Caches or Arrays\n\nData structures used for caching (like objects, Arrays, or Maps) that grow without limits.\n\n**Fix:** Implement caching limits, use LRU caches, or use `WeakMap`/`WeakSet` for data associated with object lifecycles.\n","isInternal":false,"tokens":392,"sizeBytes":1813},{"name":"SKILL.md","path":"skills/memory-leak-debugging/SKILL.md","rawUrl":"https://raw.githubusercontent.com/ChromeDevTools/chrome-devtools-mcp/HEAD/skills/memory-leak-debugging/SKILL.md","title":"memory-leak-debugging","category":"anthropic-skill","format":"markdown","content":"---\nname: memory-leak-debugging\ndescription: Diagnoses and resolves memory leaks in JavaScript/Node.js applications. Use when a user reports high memory usage, OOM errors, or wants to capture, compare, or inspect heap snapshots with Chrome DevTools MCP memory tools.\n---\n\n# Memory Leak Debugging\n\nThis skill provides expert guidance and workflows for finding, diagnosing, and fixing memory leaks in JavaScript and Node.js applications using Chrome DevTools MCP tools.\n\n## Prerequisites\n\nAdvanced memory debugging tools (`compare_heapsnapshots`, `get_heapsnapshot_details`, etc.) are only available when the server is started with the `--memoryDebugging` flag. First check if these tools are available; if not, try to read the MCP configuration file to check if `--memoryDebugging` is enabled.\n\n## Core Principles\n\n- **Prefer MCP memory tools:** Do NOT attempt to read raw `.heapsnapshot` files directly, as they are extremely large and will consume too many tokens. Use the Chrome DevTools MCP heap snapshot tools to summarize, compare, and inspect snapshots.\n- **Isolate the Leak:** Determine if the leak is in the browser (client-side) or Node.js (server-side).\n- **Common Culprits:** Look for detached DOM nodes, unhandled closures, global variables, event listeners not being removed, and caches growing unbounded. _Note: Detached DOM nodes are sometimes intentional caches; always ask the user before nulling them._\n- **Close Loaded Snapshots:** Heap snapshots can be large. After completing an investigation, use `close_heapsnapshot` for each loaded snapshot to release memory held by the MCP server.\n\n## Workflows\n\n### 1. Capturing Snapshots\n\nWhen investigating a frontend web application memory leak, utilize the `chrome-devtools-mcp` tools to interact with the application and take snapshots.\n\n- Use page-scoped tools like `click`, `navigate_page`, `fill`, etc. (specifying `pageId`) to manipulate the page into the desired state.\n- Revert the page back to the original state after interactions to see if memory is released.\n- Repeat the same user interactions 10 times to amplify the leak.\n- Use `take_heapsnapshot` (with `pageId`) to save `.heapsnapshot` files to disk at baseline, target (after actions), and final (after reverting actions) states.\n\n### 2. Comparing Snapshots\n\nOnce you have generated `.heapsnapshot` files using `take_heapsnapshot`, compare them with Chrome DevTools MCP memory tools.\n\n- Start with `get_heapsnapshot_summary` for each snapshot to confirm that the files load and to compare high-level totals.\n- Use `compare_heapsnapshots` to compare baseline and target snapshots. Start without `classIndex` for the summary diff, then request detailed class diffs only for suspicious growth by specifying `classIndex`.\n- Use the summary output from `compare_heapsnapshots` before drilling into specific node IDs.\n\n### 3. Inspecting Retainers and Dominator Chains\n\nWhen a class or object type grows unexpectedly, inspect the retaining chain and dominators with the MCP tools before changing code.\n\n- Use `get_heapsnapshot_class_nodes` to list instances of the suspicious class.\n- Use `get_heapsnapshot_retainers`, `get_heapsnapshot_retaining_paths`, `get_heapsnapshot_dominators`, and `get_heapsnapshot_edges` to understand why representative nodes are still reachable.\n- Use `get_heapsnapshot_object_details` with a specific `nodeId` to retrieve detailed object metadata (size, type, distance, and DOM detachedness).\n- Use `get_heapsnapshot_duplicate_strings` when string growth dominates the diff.\n- Read [references/common-leaks.md](references/common-leaks.md) for examples of common memory leaks and how to fix them after the retaining path points at application code.\n\n### 4. Advanced Analysis and Categorized Filters\n\nUse built-in MCP memory tools and filters to pinpoint specific leak categories directly without external tools.\n\n- Use `get_heapsnapshot_details` or `get_heapsnapshot_class_nodes` with `filterName` to target common leak causes:\n  - `objectsRetainedByDetachedDomNodes`: Identifies detached DOM elements retained in memory.\n  - `objectsRetainedByEventHandlers`: Identifies objects kept alive by unremoved event listeners.\n  - `objectsRetainedByContexts`: Identifies objects trapped in closures or execution contexts.\n  - `objectsRetainedByConsole`: Identifies objects retained by console logging.\n","frontmatter":{"name":"memory-leak-debugging","description":"Diagnoses and resolves memory leaks in JavaScript/Node.js applications. Use when a user reports high memory usage, OOM errors, or wants to capture, compare, or inspect heap snapshots with Chrome DevTools MCP memory tools."},"isInternal":false,"tokens":937,"sizeBytes":4348},{"name":"SKILL.md","path":"skills/troubleshooting/SKILL.md","rawUrl":"https://raw.githubusercontent.com/ChromeDevTools/chrome-devtools-mcp/HEAD/skills/troubleshooting/SKILL.md","title":"troubleshooting","category":"anthropic-skill","format":"markdown","content":"---\nname: troubleshooting\ndescription: Uses Chrome DevTools MCP and documentation to troubleshoot connection and target issues. Trigger this skill when list_pages, new_page, or navigate_page fail, or when the server initialization fails.\n---\n\n## Troubleshooting Wizard\n\nYou are acting as a troubleshooting wizard to help the user configure and fix their Chrome DevTools MCP server setup. When this skill is triggered (e.g., because `list_pages`, `new_page`, or `navigate_page` failed, or the server wouldn't start), follow this step-by-step diagnostic process:\n\n### Step 1: Find and Read Configuration\n\nYour first action should be to locate and read the MCP configuration file. Search for the following files in the user's workspace: `.mcp.json`, `.vscode/mcp.json`, `gemini-extension.json`, or `.gemini/settings.json` (or user-scoped files such as `~/.claude.json` if accessible).\n\nIf you find a configuration file, read and interpret it to identify potential issues such as:\n\n- Incorrect arguments or flags.\n- Missing environment variables.\n- Usage of `--autoConnect` in incompatible environments.\n\nIf you cannot find any of these files, only then should you ask the user to provide their configuration file content.\n\n### Step 2: Triage Common Connection Errors\n\nBefore reading documentation or suggesting configuration changes, check if the error message matches one of the following common patterns.\n\n#### Error: `Could not find DevToolsActivePort`\n\nThis error is highly specific to the `--autoConnect` feature. It means the MCP server cannot find the file created by a running, debuggable Chrome instance. This is not a generic connection failure.\n\nYour primary goal is to guide the user to ensure Chrome is running and properly configured. Do not immediately suggest switching to `--browserUrl`. Follow this exact sequence:\n\n1. **Ask the user to confirm that the correct Chrome version** (e.g., \"Chrome Canary\" if the error mentions it) is currently running.\n2. **If the user confirms it is running, instruct them to enable remote debugging.** Be very specific about the URL and the action: \"Please open a new tab in Chrome, navigate to `chrome://inspect/#remote-debugging`, and make sure the 'Enable remote debugging' checkbox is checked.\"\n3. **Once the user confirms both steps, your only next action should be to call the `list_pages` tool.** This is the simplest and safest way to verify if the connection is now successful. Do not retry the original, more complex command yet.\n4. **If `list_pages` succeeds, the problem is resolved.** If it still fails with the same error, then you can proceed to the more advanced steps like suggesting `--browserUrl` or checking for sandboxing issues.\n\n#### Symptom: Server starts but creates a new empty profile\n\nIf the server starts successfully but `list_pages` returns an empty list or creates a new profile instead of connecting to the existing Chrome instance, check for typos in the arguments.\n\n- **Check for flag typos:** For example, `--autoBronnect` instead of `--autoConnect`.\n- **Verify the configuration:** Ensure the arguments match the expected flags exactly.\n\n#### Symptom: Missing Tools / Only a few tools available\n\nIf the server starts successfully but only a limited subset of tools (like `list_pages`, `list_console_messages`, `get_console_message`, `list_network_requests`) are available, this is likely because the MCP client is enforcing a **read-only mode**.\n\nAll tools in `chrome-devtools-mcp` are annotated with `readOnlyHint: true` (for safe, non-modifying tools) or `readOnlyHint: false` (for tools that modify browser state, like `emulate`, `click`, `navigate_page`). To access the full suite of tools, the user must disable read-only mode in their MCP client (e.g., by exiting \"Plan Mode\" in Gemini CLI or adjusting their client's tool safety settings).\n\n#### Symptom: Extension tools are missing or extensions fail to load\n\nIf the tools related to extensions (like `install_extension`) are not available, or if the extensions you load are not functioning:\n\n1. **Check for the `--categoryExtensions` flag**: Ensure this flag is passed in the MCP server configuration to enable the extension category tools.\n\n#### Other Common Errors\n\nIdentify other error messages from the failed tool call or the MCP initialization logs:\n\n- `Target closed`\n- \"Tool not found\" (check if they are using `--slim` which only enables navigation, script evaluation and screenshot tools).\n- Missing `pageId`: Page-scoped tools require a `pageId` argument. Call `list_pages` to find active page IDs.\n- `ProtocolError: Network.enable timed out` or `The socket connection was closed unexpectedly`\n- `Error [ERR_MODULE_NOT_FOUND]: Cannot find module`\n- Any sandboxing or host validation errors.\n\n### Step 3: Read Known Issues\n\nRead the contents of https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/troubleshooting.md to map the error to a known issue. Pay close attention to:\n\n- Sandboxing restrictions (macOS Seatbelt, Linux containers).\n- WSL requirements.\n- `--autoConnect` handshakes, timeouts, and requirements (requires **running** Chrome 144+).\n\n### Step 4: Formulate a Configuration\n\nBased on the exact error and the user's environment (OS, MCP client), formulate the correct MCP configuration snippet. Check if they need to:\n\n- Pass `--browser-url=http://127.0.0.1:9222` instead of `--autoConnect` (e.g. if they are in a sandboxed environment like Claude Desktop).\n- Enable remote debugging in Chrome (`chrome://inspect/#remote-debugging`) and accept the connection prompt. **Ask the user to verify this is enabled if using `--autoConnect`.**\n- Add `--logFile <absolute_path_to_log_file>` to capture debug logs for analysis.\n- Increase `startup_timeout_ms` (e.g. to 20000) if using Codex on Windows.\n\n_If you are unsure of the user's configuration, ask the user to provide their current MCP server JSON configuration._\n\n### Step 5: Run Diagnostic Commands\n\nIf the issue is still unclear, run diagnostic commands to test the server directly:\n\n- Run `npx chrome-devtools-mcp@latest --help` to verify the installation and Node.js environment.\n- If you need more information, run `NODE_DEBUG=* npx chrome-devtools-mcp@latest --logFile=/tmp/cdm-test.log` to capture verbose logs. Analyze the output for errors.\n\n### Step 6: Check GitHub for Existing Issues\n\nIf https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/troubleshooting.md does not cover the specific error, check if the `gh` (GitHub CLI) tool is available in the environment. If so, search the GitHub repository for similar issues:\n`gh issue list --repo ChromeDevTools/chrome-devtools-mcp --search \"<error snippet>\" --state all`\n\nAlternatively, you can recommend that the user checks https://github.com/ChromeDevTools/chrome-devtools-mcp/issues and https://github.com/ChromeDevTools/chrome-devtools-mcp/discussions for help.\n","frontmatter":{"name":"troubleshooting","description":"Uses Chrome DevTools MCP and documentation to troubleshoot connection and target issues. Trigger this skill when list_pages, new_page, or navigate_page fail, or when the server initialization fails."},"isInternal":false,"tokens":1515,"sizeBytes":6876},{"name":"marketplace.json","path":".claude-plugin/marketplace.json","rawUrl":"https://raw.githubusercontent.com/ChromeDevTools/chrome-devtools-mcp/HEAD/.claude-plugin/marketplace.json","title":"chrome-devtools-plugins","category":"marketplace","format":"json","content":"{\n  \"name\": \"chrome-devtools-plugins\",\n  \"version\": \"1.12.0\",\n  \"description\": \"Bundled plugins for actuating and debugging the Chrome browser.\",\n  \"repository\": \"https://github.com/ChromeDevTools/chrome-devtools-mcp\",\n  \"owner\": {\n    \"name\": \"Chrome DevTools Team\",\n    \"email\": \"devtools-dev@chromium.org\"\n  },\n  \"plugins\": [\n    {\n      \"name\": \"chrome-devtools-mcp\",\n      \"source\": \"./\",\n      \"description\": \"Reliable automation, in-depth debugging, and performance analysis in Chrome using Chrome DevTools and Puppeteer\"\n    }\n  ]\n}\n","frontmatter":{"name":"chrome-devtools-plugins","version":"1.12.0","description":"Bundled plugins for actuating and debugging the Chrome browser.","repository":"https://github.com/ChromeDevTools/chrome-devtools-mcp","owner":{"name":"Chrome DevTools Team","email":"devtools-dev@chromium.org"},"plugins":[{"name":"chrome-devtools-mcp","source":"./","description":"Reliable automation, in-depth debugging, and performance analysis in Chrome using Chrome DevTools and Puppeteer"}]},"isInternal":false,"tokens":145,"sizeBytes":541},{"name":"plugin.json","path":".claude-plugin/plugin.json","rawUrl":"https://raw.githubusercontent.com/ChromeDevTools/chrome-devtools-mcp/HEAD/.claude-plugin/plugin.json","title":"chrome-devtools-mcp","category":"plugin-manifest","format":"json","content":"{\n  \"name\": \"chrome-devtools-mcp\",\n  \"version\": \"1.12.0\",\n  \"description\": \"Reliable automation, in-depth debugging, and performance analysis in Chrome using Chrome DevTools and Puppeteer\",\n  \"mcpServers\": {\n    \"chrome-devtools\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"chrome-devtools-mcp@1.12.0\"\n      ]\n    }\n  }\n}\n","frontmatter":{"name":"chrome-devtools-mcp","version":"1.12.0","description":"Reliable automation, in-depth debugging, and performance analysis in Chrome using Chrome DevTools and Puppeteer","mcpServers":{"chrome-devtools":{"command":"npx","args":["chrome-devtools-mcp@1.12.0"]}}},"isInternal":false,"tokens":97,"sizeBytes":330},{"name":"plugin.json","path":".cursor-plugin/plugin.json","rawUrl":"https://raw.githubusercontent.com/ChromeDevTools/chrome-devtools-mcp/HEAD/.cursor-plugin/plugin.json","title":"devtools-for-agents","category":"plugin-manifest","format":"json","content":"{\n  \"name\": \"devtools-for-agents\",\n  \"description\": \"Help your agent build, debug, and verify your code correctly. With Chrome DevTools for agents, your AI agent can interact with the Chrome browser to test code, emulate users, and catch bugs using Chrome DevTools’ capabilities before shipping.\",\n  \"version\": \"1.12.0\",\n  \"author\": {\n    \"name\": \"Google Chrome\"\n  },\n  \"logo\": \"https://github.com/ChromeDevTools/devtools-logo/blob/master/logos/svg/chrome-devtools-square-responsive.svg\",\n  \"homepage\": \"https://developer.chrome.com/docs/devtools/agents\",\n  \"repository\": \"https://github.com/ChromeDevTools/chrome-devtools-mcp\",\n  \"mcpServers\": {\n    \"chrome-devtools\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"chrome-devtools-mcp@1.12.0\"\n      ]\n    }\n  }\n}\n","frontmatter":{"name":"devtools-for-agents","description":"Help your agent build, debug, and verify your code correctly. With Chrome DevTools for agents, your AI agent can interact with the Chrome browser to test code, emulate users, and catch bugs using Chrome DevTools’ capabilities before shipping.","version":"1.12.0","author":{"name":"Google Chrome"},"logo":"https://github.com/ChromeDevTools/devtools-logo/blob/master/logos/svg/chrome-devtools-square-responsive.svg","homepage":"https://developer.chrome.com/docs/devtools/agents","repository":"https://github.com/ChromeDevTools/chrome-devtools-mcp","mcpServers":{"chrome-devtools":{"command":"npx","args":["chrome-devtools-mcp@1.12.0"]}}},"isInternal":false,"tokens":204,"sizeBytes":771},{"name":"plugin.json","path":".github/plugin/plugin.json","rawUrl":"https://raw.githubusercontent.com/ChromeDevTools/chrome-devtools-mcp/HEAD/.github/plugin/plugin.json","title":"chrome-devtools-mcp","category":"plugin-manifest","format":"json","content":"{\n  \"name\": \"chrome-devtools-mcp\",\n  \"version\": \"1.12.0\",\n  \"description\": \"Reliable automation, in-depth debugging, and performance analysis in Chrome using Chrome DevTools and Puppeteer\",\n  \"mcpServers\": {\n    \"chrome-devtools\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"chrome-devtools-mcp@1.12.0\"\n      ]\n    }\n  }\n}\n","frontmatter":{"name":"chrome-devtools-mcp","version":"1.12.0","description":"Reliable automation, in-depth debugging, and performance analysis in Chrome using Chrome DevTools and Puppeteer","mcpServers":{"chrome-devtools":{"command":"npx","args":["chrome-devtools-mcp@1.12.0"]}}},"isInternal":false,"tokens":97,"sizeBytes":330},{"name":"plugin.json","path":".kimi-code-plugin/plugin.json","rawUrl":"https://raw.githubusercontent.com/ChromeDevTools/chrome-devtools-mcp/HEAD/.kimi-code-plugin/plugin.json","title":"chrome-devtools","category":"plugin-manifest","format":"json","content":"{\n  \"name\": \"chrome-devtools\",\n  \"description\": \"Help your agent build, debug, and verify your code correctly. With Chrome DevTools for agents, your AI agent can interact with the Chrome browser to test code, emulate users, and catch bugs using Chrome DevTools’ capabilities before shipping.\",\n  \"version\": \"1.12.0\",\n  \"author\": {\n    \"name\": \"Google Chrome\"\n  },\n  \"logo\": \"https://github.com/ChromeDevTools/devtools-logo/blob/master/logos/svg/chrome-devtools-square-responsive.svg\",\n  \"homepage\": \"https://developer.chrome.com/docs/devtools/agents\",\n  \"repository\": \"https://github.com/ChromeDevTools/chrome-devtools-mcp\",\n  \"mcpServers\": {\n    \"chrome-devtools\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"chrome-devtools-mcp@1.12.0\"\n      ]\n    }\n  }\n}\n","frontmatter":{"name":"chrome-devtools","description":"Help your agent build, debug, and verify your code correctly. With Chrome DevTools for agents, your AI agent can interact with the Chrome browser to test code, emulate users, and catch bugs using Chrome DevTools’ capabilities before shipping.","version":"1.12.0","author":{"name":"Google Chrome"},"logo":"https://github.com/ChromeDevTools/devtools-logo/blob/master/logos/svg/chrome-devtools-square-responsive.svg","homepage":"https://developer.chrome.com/docs/devtools/agents","repository":"https://github.com/ChromeDevTools/chrome-devtools-mcp","mcpServers":{"chrome-devtools":{"command":"npx","args":["chrome-devtools-mcp@1.12.0"]}}},"isInternal":false,"tokens":202,"sizeBytes":767},{"name":"plugin.json","path":"plugin.json","rawUrl":"https://raw.githubusercontent.com/ChromeDevTools/chrome-devtools-mcp/HEAD/plugin.json","title":"chrome-devtools","category":"plugin-manifest","format":"json","content":"{\n  \"$schema\": \"https://agent-plugins.org/schemas/1.0.0/plugin.schema.json\",\n  \"name\": \"chrome-devtools\",\n  \"version\": \"1.12.0\",\n  \"description\": \"Reliable Chrome automation, debugging, and performance analysis for AI agents.\",\n  \"author\": {\n    \"name\": \"Google Chrome\",\n    \"url\": \"https://developer.chrome.com/\"\n  },\n  \"homepage\": \"https://developer.chrome.com/docs/devtools/agents\",\n  \"repository\": \"https://github.com/ChromeDevTools/chrome-devtools-mcp\",\n  \"license\": \"Apache-2.0\",\n  \"keywords\": [\n    \"browser\",\n    \"chrome\",\n    \"debugging\",\n    \"mcp\",\n    \"performance\"\n  ]\n}\n","frontmatter":{"$schema":"https://agent-plugins.org/schemas/1.0.0/plugin.schema.json","name":"chrome-devtools","version":"1.12.0","description":"Reliable Chrome automation, debugging, and performance analysis for AI agents.","author":{"name":"Google Chrome","url":"https://developer.chrome.com/"},"homepage":"https://developer.chrome.com/docs/devtools/agents","repository":"https://github.com/ChromeDevTools/chrome-devtools-mcp","license":"Apache-2.0","keywords":["browser","chrome","debugging","mcp","performance"]},"isInternal":false,"tokens":165,"sizeBytes":583}],"systemPromptSnippet":"<yakaai_skills repo=\"ChromeDevTools/chrome-devtools-mcp\">\n<!-- File: AGENTS.md (Tokens: ~2981 | Category: root-instruction) -->\nThis repository contains an MCP server and CLI for Chrome DevTools.\n\n# Instructions\n\n- Use only scripts from `package.json` to run commands.\n- Use `npm run build` to run tsc and test build.\n- Use `npm run test` to build and run tests, run all tests to verify correctness.\n- Use `npm run test path-to-test.ts` to build and run a single test file, for example, `npm run test tests/McpContext.test.ts`.\n- Use `npm run format` to fix formatting and get linting errors.\n- Never modify `third_party/devtools-frontend` except for experimentation: it is a git submodule, a mirror of the actual codebase.\n\n## Rules for TypeScript\n\n- Do not use `any` type.\n- Do not use `as` keyword for type casting.\n- Do not use `!` operator for type assertion.\n- Do not use `// @ts-ignore` comments.\n- Do not use `// @ts-nocheck` comments.\n- Do not use `// @ts-expect-error` comments.\n- Prefer `for..of` instead of `forEach`.\n- Never type-check types that are already type safe (e.g. redundant `typeof` checks on statically typed variables).\n\n## Rules for Testing\n\n### Structure and Separation of Concerns\n\n- **Prefer mock-based unit tests over real-browser tests**: Do not use `withMcpContext` or launch a real browser unless the test genuinely requires real browser or DevTools protocol integration (e.g., live CDP events, browser lifecycle, secondary sessions). Puppeteer already tests browser behavior upstream; unit tests run faster and avoid browser overhead.\n- **Tool handler tests (`tests/tools/*.test.ts`)**:\n  - Test that the tool handler parses/validates parameters and invokes the corresponding methods on `page`, `context`, or `response` with the exact expected arguments.\n  - Do **not** reimplement business logic or state tracking inside mocks (e.g., do not simulate state changes in mock methods).\n- **Core class tests (e.g., `tests/McpPage.test.ts`)**:\n  - Test business logic by instantiating the real class under test with mocked dependencies (e.g., instantiate `new McpPage(...)` with a mocked Puppeteer page from `createMockPuppeteerPage()`).\n  - Assert that the class calls the underlying Puppeteer methods with the expected parameters.\n\n### Mocking Guidelines (`tests/mocks.ts`)\n\n- **Centralize mocks in `tests/mocks.ts`**: Keep all reusable mock factories in `tests/mocks.ts`. Import directly from `tests/mocks.ts` (do not re-export from `tests/utils.ts`).\n- **Use `sinon.createStubInstance(Class)`**: Do not hand-roll mock objects or define custom mock interfaces. Use `sinon.createStubInstance()` so all prototype methods are automatically stubbed.\n- **Typing**: Use `sinon.SinonStubbedInstance<Class>` for mock types (e.g., `MockMcpPage`, `MockMcpContext`, `MockMcpResponse`).\n- **Handler mocks helper**: For tool handlers, use `const {page, context, response} = createHandlerMocks();` from `tests/mocks.ts` to set up all three mocks in one call.\n- **Keep mocks generic**: Do not tailor mocks to a specific tool or test suite.\n- **Naming conventions**: Use `mock` rather than `fake` in helper and variable names (e.g., `createMockPuppeteerPage`, `createMockMcpPage`). Name the mocked Puppeteer page instance `pptrPage`.\n\n### Assertions and Sinon Best Practices\n\n- **Use `sinon.assert` methods**: Do not use Node's `assert.ok(stub.calledOnce)` or `assert.deepStrictEqual(stub.firstCall.args[0], ...)` to verify stub calls.\n- **Verify exact arguments**:\n  - Use `sinon.assert.calledOnceWithExactly(stub, ...args)` for single calls with exact arguments.\n  - Use `sinon.assert.calledWithExactly(stub.secondCall, ...args)` for subsequent calls.\n  - Use `sinon.assert.notCalled(stub)` to assert a method was not invoked.\n- **Clean up stubs**: `tests/setup.ts` runs `sinon.restore()` after every test. Do not add `afterEach(() => sinon.restore());` to test suites.\n\n### Test Cleanliness\n\n- Do not add redundant comments or verbose JSDoc for short, self-describing mock functions or tests.\n- Only test real scenarios; avoid testing redundant or artificial calls that cannot happen in real usage.\n- Use current year (2026) in copyright headers for new test files.\n\n# Pre-submit checklist\n\nComplete every item before you upload a PR or push a new revision.\n\n## Scope and PR description\n\n- [ ] The PR contains one change. Move unrelated refactors, renames,\n      formatting changes, and build or compiler changes into their own PR.\n- [ ] The PR title, the commit type (`fix:`, `feat:`, `chore:`), and the\n      squash commit message describe the same change. A change to a default\n      value or to tool output is a `fix:` or a `feat:`, not a `chore:`.\n- [ ] The PR description lists every user-visible behavior change, including\n      changes to other tools that share the changed code path.\n- [ ] The PR description matches the shipped behavior. Remove claims about\n      fixes that the diff does not contain.\n- [ ] The PR description names new functions that have no production caller\n      yet, or the PR removes them.\n- [ ] The branch has no merge conflicts with `main`. Rebase only when a\n      conflict exists.\n- [ ] `npm run gen` ran after a change to a tool schema, a tool description,\n      or a CLI option. The generated files are committed.\n- [ ] `npm run format` ran. No line is longer than 80 columns.\n- [ ] `npm run test` passed locally. CI failures on all platforms are your\n      responsibility.\n\n## Design\n\n- [ ] The change lives in the lowest layer that owns the data: page logic in\n      `McpPage`, context logic in `McpContext`, formatting in\n      `src/formatters/*`. `McpResponse` does not resolve pages or workers.\n- [ ] If Puppeteer or devtools-frontend owns the bug, fix it there first. Do\n      not copy or re-implement upstream code in this repository.\n- [ ] Puppeteer internals are imported only through `src/third_party/index.ts`.\n- [ ] Repeated code is one helper. Extract a helper when the same block\n      appears two or more times. Prefer an existing helper or a Puppeteer\n      utility (for example `Deferred`) over a new one.\n- [ ] A new formatter class exposes `toJSON()` and `toString()`, and\n      `toString()` uses the `toJSON()` output.\n- [ ] A function with three or more optional parameters takes one options\n      object. Do not pass positional `undefined` placeholders.\n- [ ] Each default value is declared in one place.\n- [ ] A new behavior is opt-in by default when a wrong classification fails\n      silently (for example a config option is restart-required unless the\n      author marks it reloadable).\n- [ ] An invalid combination of flags or options is rejected by the type\n      system where possible, not only by a comment.\n- [ ] No public method exists only to serve a test. Put shared helpers in\n      `src/utils/*`.\n- [ ] Parameter validation lives in the Zod schema (`refine`, `coerce`), not\n      in the handler. Validation runs before the handler changes any state.\n- [ ] Numeric validation matches what the consumer accepts. Use\n      `Number.isInteger` and a range check where CDP expects integers.\n\n## Correctness and edge cases\n\n- [ ] A failed external call does not leave stale state. Restore the previous\n      value when a call rejects.\n- [ ] An error path still produces the artifacts and partial results the\n      agent needs before it throws.\n- [ ] An async operation that a timeout can abandon checks that its target\n      (browser, page, config) is still current when it resumes.\n- [ ] A lock or guard is acquired and released exactly once. A helper that\n      releases a guard throws when the guard is already released.\n- [ ] Concurrent callbacks (watchers, reloads, event handlers) are\n      serialized. A transient failure does not trigger a callback.\n- [ ] A filesystem operation that races with another process tolerates the\n      race (`fs.rmSync(path, {force: true})` or `try/catch`), not\n      `existsSync` + an unguarded call.\n- [ ] A failed lookup falls back to the previous behavior. It does not turn\n      a healthy state into an error state.\n- [ ] No synchronous process spawn or blocking call runs in a polling loop\n      or in a hot function.\n- [ ] `dispose()` releases every resource the object created, and runs for\n      objects that are already closed.\n- [ ] Event listeners added for one action are removed when the action ends.\n- [ ] Cached page-side state is refreshed before use. The page can reload\n      between tool calls.\n- [ ] Behavior shared by all tools that act on the selected page also runs\n      for tools defined with `defineTool`, not only `definePageTool`.\n- [ ] String and URL parsing handles userinfo, escaped delimiters, ports,\n      pattern groups, and empty or whitespace-only input. Use the full\n      authority, not a heuristic on the first or last character.\n- [ ] Truncation uses `Intl.Segmenter` graphemes, not `string.slice`.\n- [ ] Secrets never reach logs. Redact headers, URL userinfo, and query\n      strings in every log line, including process startup arguments.\n- [ ] A filesystem path parameter is validated with `validatePath()` even\n      when the value parses as a URL. The error says what the parameter\n      expects.\n- [ ] A value that crosses a process boundary (daemon IPC, CLI) is\n      serialized with `JSON.stringify` when it is not a primitive.\n- [ ] An identifier is issued only for items that a tool can resolve later.\n- [ ] A dedup key never collapses for items without identifying fields.\n      Use a counter or skip such items.\n- [ ] An error message names the exact condition that failed, with the\n      relevant values (pid, timeout, URL, parameter name).\n- [ ] A thrown non-`Error` value keeps its content in the error message.\n      Treat objects with a string `message` like `Error`.\n- [ ] Optional chaining and `?? default` are not applied to values whose\n      type is non-optional.\n- [ ] Version-dependent behavior (Node, Chrome, OS) is tested on every\n      version the `engines` field allows, or the test does not depend on it.\n\n## Tests\n\n- [ ] Every new or changed function has a test that calls it. A function\n      with no caller in `src/` and no test is removed from the PR.\n- [ ] Every review request of the form \"add a test for X\" has a test whose\n      name includes X.\n- [ ] A new test for a bug fails on `main` without the fix. Remove\n      assertions that pass with and without the fix.\n- [ ] A `notCalled` assertion targets the first method the handler calls\n      after the step under test, not a method inside a callback that a stub\n      never runs.\n- [ ] A test for a selected branch also asserts that the other branch was\n      not taken.\n- [ ] Assertions use `calledOnceWithExactly` or\n      `calledWithExactly(stub.nthCall, ...)`. Use `calledOnceWithMatch` only\n      for arguments that cannot be exact (for example an `AbortSignal`) or\n      when an overloaded signature forces `sinon.match.same`.\n- [ ] Options that the handler derives from its input are asserted, not\n      only the call count.\n- [ ] Test names describe the asserted behavior. Rename the test when the\n      assertion changes.\n- [ ] `if (value) { assert... }` does not appear in tests. Use\n      `assert.ok(value)` before the dependent assertions.\n- [ ] Real-browser tests exist only for behavior that mocks cannot observe.\n      A test file contains at most one slow end-to-end run per scenario.\n- [ ] A real-browser test that counts requests filters `favicon.ico`.\n- [ ] A real-browser test that waits for CDP events waits on the session or\n      result of the code under test, not on a new `createCDPSession()`.\n- [ ] Every awaited event in a test has a bounded timeout. Positive-path\n      tests poll for the condition instead of a fixed sleep.\n- [ ] New reusable stubs and fixtures live in `tests/mocks.ts` and build on\n      `sinon.createStubInstance`. No hand-rolled fixture uses `as unknown as`.\n- [ ] Mock options that equal the default value are not passed.\n- [ ] Temporary directories use `using dir = createTempDir(...)` from\n      `tests/utils.ts`, not `fs.mkdtemp`.\n- [ ] A test never reads the developer's home directory and never calls\n      `process.exit`. Stub locators and pass `exitProcess = false`.\n- [ ] When a real-browser test is replaced with mocks, each branch the old\n      test covered still has a test.\n- [ ] A test does not repeat a round-trip that another test already covers.\n      Stop after the first assertion that proves the change.\n\n## Documentation and output\n\n- [ ] A tool description states the exact behavior, including when a\n      parameter is ignored or depends on another parameter.\n- [ ] Skills stay generic. Add one short note in the tool-selection section\n      instead of a feature-specific workflow.\n- [ ] Inline comments are one sentence. Remove comments that repeat an\n      existing TSDoc.\n- [ ] Response text that depends on a condition prints only when the\n      condition is true.\n\n<!-- File: skills/a11y-debugging/references/a11y-snippets.md (Tokens: ~767 | Category: anthropic-skill) -->\n# Accessibility Debugging Snippets\n\nUse these JavaScript snippets with the `evaluate_script` tool.\n\n## 1. Find Orphaned Form Inputs\n\nFinds form inputs that lack an associated label (no `label[for]`, `aria-label`, `aria-labelledby`, or wrapping `<label>`).\n\n```js\n() =>\n  Array.from(document.querySelectorAll('input, select, textarea'))\n    .filter(i => {\n      const hasId = i.id && document.querySelector(`label[for=\"${i.id}\"]`);\n      const hasAria =\n        i.getAttribute('aria-label') || i.getAttribute('aria-labelledby');\n      return !hasId && !hasAria && !i.closest('label');\n    })\n    .map(i => ({\n      tag: i.tagName,\n      id: i.id,\n      name: i.name,\n      placeholder: i.placeholder,\n    }));\n```\n\n## 2. Measure Tap Target Size\n\nReturns the bounding box dimensions of an element. Pass the element's `uid` from the snapshot as an argument to `evaluate_script`.\n\n```js\nel => {\n  const rect = el.getBoundingClientRect();\n  return {width: rect.width, height: rect.height};\n};\n```\n\n## 3. Check Color Contrast\n\nApproximates the contrast ratio between an element's text color and background color. Pass the element's `uid` to test against WCAG AA (4.5:1 for normal text, 3:1 for large text).\n\n**Note**: This uses a simplified algorithm and may not account for transparency, gradients, or background images. For production-grade auditing, consider injecting `axe-core`.\n\n```js\nel => {\n  function getRGB(colorStr) {\n    const match = colorStr.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/);\n    return match\n      ? [parseInt(match[1]), parseInt(match[2]), parseInt(match[3])]\n      : [255, 255, 255];\n  }\n  function luminance(r, g, b) {\n    const a = [r, g, b].map(function (v) {\n      v /= 255;\n      return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);\n    });\n    return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;\n  }\n\n  const style = window.getComputedStyle(el);\n  const fg = getRGB(style.color);\n  let bg = getRGB(style.backgroundColor);\n\n  const l1 = luminance(fg[0], fg[1], fg[2]);\n  const l2 = luminance(bg[0], bg[1], bg[2]);\n  const ratio = (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);\n\n  return {\n    color: style.color,\n    bg: style.backgroundColor,\n    contrastRatio: ratio.toFixed(2),\n  };\n};\n```\n\n## 4. Global Page Checks\n\nChecks document-level accessibility settings often missed in component testing.\n\n```js\n() => ({\n  lang:\n    document.documentElement.lang ||\n    'MISSING - Screen readers need this for pronunciation',\n  title: document.title || 'MISSING - Required for context',\n  viewport:\n    document.querySelector('meta[name=\"viewport\"]')?.content ||\n    'MISSING - Check for user-scalable=no (bad practice)',\n  reducedMotion: window.matchMedia('(prefers-reduced-motion: reduce)').matches\n    ? 'Enabled'\n    : 'Disabled',\n});\n```\n\n<!-- File: skills/a11y-debugging/SKILL.md (Tokens: ~1369 | Category: anthropic-skill) -->\n---\nname: a11y-debugging\ndescription: Uses Chrome DevTools MCP for accessibility (a11y) debugging and auditing based on web.dev guidelines. Use when testing semantic HTML, ARIA labels, focus states, keyboard navigation, tap targets, and color contrast.\n---\n\n## Core Concepts\n\n**Page Targeting**: Page-scoped tools (`take_snapshot`, `list_console_messages`, `evaluate_script`, `press_key`, `take_screenshot`, `lighthouse_audit`, etc.) require a `pageId` parameter. Retrieve available page IDs using `list_pages` or from `new_page`.\n\n**Accessibility Tree vs DOM**: Visually hiding an element (e.g., `CSS opacity: 0`) behaves differently for screen readers than `display: none` or `aria-hidden=\"true\"`. The `take_snapshot` tool returns the accessibility tree of the page, which represents what assistive technologies \"see\", making it the most reliable source of truth for semantic structure.\n\n**Reading web.dev documentation**: If you need to research specific accessibility guidelines (like `https://web.dev/articles/accessible-tap-targets`), you can append `.md.txt` to the URL (e.g., `https://web.dev/articles/accessible-tap-targets.md.txt`) to fetch the clean, raw markdown version. This is much easier to read!\n\n## Workflow Patterns\n\n### 1. Automated Audit (Lighthouse)\n\nStart by running a Lighthouse accessibility audit to get a comprehensive baseline. This tool provides a high-level score and lists specific failing elements with remediation advice.\n\n1.  Run the audit:\n    - Set `mode` to `\"navigation\"` to refresh the page and capture load issues.\n    - Omit `outputDirPath` to save reports to the default temporary directory (the exact path to `report.json` is returned under `Reports`), or set `outputDirPath` to a directory within the workspace.\n2.  **Analyze the Summary**:\n    - In the default text output, category scores use a 0-100 scale (100 is a perfect score), followed by `Passed: N` and `Failed: N` audit counts.\n    - If the server was started with `--experimentalStructuredContent`, inspect `summary.scores[].score` (0-1 scale) and `summary.audits.failed` in the structured content instead.\n3.  **Review the Report (CRITICAL)**:\n    - **Parsing**: Do not read the entire file line-by-line. Use a CLI tool like `jq` or a Node.js one-liner to filter for failures from the report path returned by `lighthouse_audit`:\n      ```bash\n      # Extract failing audits with their details (replace with report.json path from tool response)\n      node -e \"const r=require('<path/to/report.json>'); Object.values(r.audits).filter(a=>a.score!==null && a.score<1).forEach(a=>console.log(JSON.stringify({id:a.id, title:a.title, items:a.details?.items})))\"\n      ```\n    - This efficiently extracts the `selector` and `snippet` of failing elements without loading the full report into context.\n\n### 2. Browser Issues & Audits\n\nChrome automatically checks for common accessibility problems. Use `list_console_messages` to check for these native audits:\n\n- `types`: `[\"issue\"]`\n- `includePreservedMessages`: `true` (to catch issues that occurred during page load)\n\nThis often reveals missing labels, invalid ARIA attributes, and other critical errors without manual investigation.\n\n### 3. Semantics & Structure\n\nThe accessibility tree exposes the heading hierarchy and semantic landmarks.\n\n1.  Navigate to the page.\n2.  Use `take_snapshot` to capture the accessibility tree.\n3.  **Check Heading Levels**: Ensure heading levels (`h1`, `h2`, `h3`, etc.) are logical and do not skip levels. The snapshot will include heading roles.\n4.  **Content Reordering**: Verify that the DOM order (which drives the accessibility tree) matches the visual reading order. Use `take_screenshot` to inspect the visual layout and compare it against the snapshot structure to catch CSS floats or absolute positioning that jumbles the logical flow.\n\n### 4. Labels, Forms & Text Alternatives\n\n1.  Locate buttons, inputs, and images in the `take_snapshot` output.\n2.  Ensure interactive elements have an accessible name (e.g., a button should not just say `\"\"` if it only contains an icon).\n3.  **Orphaned Inputs**: Verify that all form inputs have associated labels. Use `evaluate_script` with the **\"Find Orphaned Form Inputs\" snippet** found in [references/a11y-snippets.md](references/a11y-snippets.md).\n4.  Check images for `alt` text.\n\n### 5. Focus & Keyboard Navigation\n\nTesting \"keyboard traps\" and proper focus management without visual feedback relies on tracking the focused element.\n\n1.  Use the `press_key` tool with `\"Tab\"` or `\"Shift+Tab\"` to move focus.\n2.  Use `take_snapshot` to capture the updated accessibility tree.\n3.  Locate the element marked as focused in the snapshot to verify focus moved to the expected interactive element.\n4.  If a modal opens, focus must move into the modal and \"trap\" within it until closed.\n\n### 6. Tap Targets and Visuals\n\nAccording to web.dev, tap targets should be at least 48x48 pixels with sufficient spacing. Since the accessibility tree doesn't show sizes, use `evaluate_script` with the **\"Measure Tap Target Size\" snippet** found in [references/a11y-snippets.md](references/a11y-snippets.md).\n\n_Pass the element's `uid` from the snapshot as an argument to `evaluate_script`._\n\n### 7. Color Contrast\n\nTo verify color contrast ratios, use the **\"Check Color Contrast\" snippet** found in [references/a11y-snippets.md](references/a11y-snippets.md) with `evaluate_script`.\n\n### 8. Global Page Checks\n\nVerify document-level accessibility settings often missed in component testing using the **\"Global Page Checks\" snippet** found in [references/a11y-snippets.md](references/a11y-snippets.md).\n\n## Troubleshooting\n\nIf standard a11y queries fail or the `evaluate_script` snippets return unexpected results:\n\n- **Visual Inspection**: If automated scripts cannot determine contrast (e.g., text over gradient images or complex backgrounds), use `take_screenshot` to capture the element. While models cannot measure exact contrast ratios from images, they can visually assess legibility and identify obvious issues.\n\n<!-- File: skills/chrome-devtools-cli/references/installation.md (Tokens: ~204 | Category: anthropic-skill) -->\n# Installation\n\nInstall the package globally to make the `chrome-devtools` command available. You only need to do this the first time you use it.\n\n```sh\nnpm i chrome-devtools-mcp@latest -g\nchrome-devtools status # check if install worked.\n```\n\n## Troubleshooting\n\n- **Command not found:** If `chrome-devtools` is not recognized, ensure your global npm `bin` directory is in your system's `PATH`. Restart your terminal or source your shell configuration file (e.g., `.bashrc`, `.zshrc`).\n- **Permission errors:** If you encounter `EACCES` or permission errors during installation, avoid using `sudo`. Instead, use a node version manager like `nvm`, or configure npm to use a different global directory.\n- **Old version running:** Run `chrome-devtools stop && npm uninstall -g chrome-devtools-mcp` before reinstalling, or ensure the latest version is being picked up by your path.\n\n<!-- File: skills/chrome-devtools-cli/SKILL.md (Tokens: ~2905 | Category: anthropic-skill) -->\n---\nname: chrome-devtools-cli\ndescription: Use this skill to write shell scripts or run shell commands to automate tasks in the browser or otherwise use Chrome DevTools via CLI.\n---\n\nThe `chrome-devtools-mcp` CLI lets you interact with the browser from your terminal.\n\n## Setup\n\n_Note: If this is your very first time using the CLI, see [references/installation.md](references/installation.md) for setup. Installation is a one-time prerequisite and is **not** part of the regular AI workflow._\n\n## AI Workflow\n\n1. **Execute**: Run tools directly. If you don't know the target page's ID, run `chrome-devtools list_pages` to find it. The background server starts implicitly; **do not** run `start`/`status`/`stop` before each use.\n2. **Inspect**: Use `chrome-devtools take_snapshot <pageId>` to get an element `<uid>`.\n3. **Act**: Use `chrome-devtools click <pageId> <uid>`, `chrome-devtools fill <pageId> <uid> <value>`, etc. State persists across commands.\n\nSnapshot example:\n\n```\nuid=1_0 RootWebArea \"Example Domain\" url=\"https://example.com/\"\n  uid=1_1 heading \"Example Domain\" level=\"1\"\n```\n\n## Permissions & File Access\n\nBy default, the CLI has full filesystem access (`--allowUnrestrictedPaths=true`), allowing file-saving parameters (`--filePath`, `--outputDirPath`) and `upload_file` to access files anywhere on the system. Pass `--allowUnrestrictedPaths=false` if you want to restrict file access to the OS temp directory.\n\n## Command Usage\n\n```sh\nchrome-devtools <tool> [arguments] [flags]\n```\n\n- Required arguments are passed positionally; optional arguments use flags.\n- Use `--help` on any command for usage details.\n- Output defaults to plain Markdown-like text; pass `--output-format=json` for JSON.\n\n## Input Automation (<uid> from snapshot)\n\n```bash\nchrome-devtools take_snapshot 1 # Take a text snapshot of the page to get UIDs for elements\nchrome-devtools click 1 \"id\" # Clicks on the provided element\nchrome-devtools click 1 \"id\" --dblClick true --includeSnapshot true # Double clicks and returns a snapshot\nchrome-devtools drag 1 \"src\" \"dst\" # Drag an element onto another element\nchrome-devtools drag 1 \"src\" \"dst\" --includeSnapshot true # Drag an element and return a snapshot\nchrome-devtools fill 1 \"id\" \"text\" # Type text into an input, textarea, or select an option\nchrome-devtools fill 1 \"id\" \"text\" --includeSnapshot true # Fill an element and return a snapshot\nchrome-devtools handle_dialog 1 accept # Handle a browser dialog (accept/dismiss)\nchrome-devtools handle_dialog 1 dismiss --promptText \"hi\" # Dismiss a dialog with prompt text\nchrome-devtools hover 1 \"id\" # Hover over the provided element\nchrome-devtools hover 1 \"id\" --includeSnapshot true # Hover over an element and return a snapshot\nchrome-devtools press_key 1 \"Enter\" # Press a key or key combination (\"Control+A\", \"Escape\")\nchrome-devtools press_key 1 \"Control+A\" --includeSnapshot true # Press a key and return a snapshot\nchrome-devtools type_text 1 \"hello\" # Type text using keyboard into a focused input\nchrome-devtools type_text 1 \"hello\" --submitKey \"Enter\" # Type text and press a submit key\nchrome-devtools upload_file 1 \"id\" \"file.txt\" # Upload a file through a provided element\nchrome-devtools upload_file 1 \"id\" \"file.txt\" --includeSnapshot true # Upload a file and return a snapshot\n```\n\n## Navigation\n\n```bash\nchrome-devtools close_page 1 # Closes the page by its index\nchrome-devtools list_pages # Get a list of pages open in the browser\nchrome-devtools navigate_page 1 --url \"https://example.com\" # Navigates the currently selected page to a URL\nchrome-devtools navigate_page 1 --type \"reload\" --ignoreCache true # Reload page ignoring cache\nchrome-devtools navigate_page 1 --url \"https://example.com\" --timeout 5000 # Navigate with a timeout\nchrome-devtools navigate_page 1 --type \"reload\" --handleBeforeUnload \"accept\" # Handle before unload dialog\nchrome-devtools navigate_page 1 --type \"back\" --initScript \"foo()\" # Navigate back and run an init script\nchrome-devtools new_page \"https://example.com\" # Creates a new page\nchrome-devtools new_page \"https://example.com\" --background true --timeout 5000 # Create new page in background\nchrome-devtools new_page \"https://example.com\" --isolatedContext \"ctx\" # Create new page with isolated context\nchrome-devtools select_page 1 # Select a page as a context for future tool calls\nchrome-devtools select_page 1 --bringToFront true # Select a page and bring it to front\n```\n\n## Emulation\n\nEach `emulate` call replaces all previously configured emulation settings (except `--extraHttpHeaders`), so pass all desired settings in a single call.\n\n```bash\nchrome-devtools emulate 1 --networkConditions \"Offline\" # Emulate network conditions (resets other settings)\nchrome-devtools emulate 1 --cpuThrottlingRate 4 --geolocation \"0,0\" # Emulate CPU throttling and geolocation (resets other settings)\nchrome-devtools emulate 1 --colorScheme \"dark\" --viewport \"1920x1080\" # Emulate color scheme and viewport (resets other settings)\nchrome-devtools emulate 1 --userAgent \"Mozilla/5.0...\" # Emulate user agent (resets other settings)\nchrome-devtools resize_page 1 1920 1080 # Resizes the selected page's window\n```\n\n## Performance\n\n```bash\nchrome-devtools performance_analyze_insight 1 \"NAVIGATION_0\" \"LCPBreakdown\" # Get more details on a specific Performance Insight (pageId, insightSetId, insightName)\nchrome-devtools performance_start_trace 1 --reload true --autoStop false # Starts a performance trace recording (reload, autoStop)\nchrome-devtools performance_start_trace 1 --reload true --autoStop true --filePath \"t.json.gz\" # Start trace and save to a file\nchrome-devtools performance_stop_trace 1 # Stops the active performance trace\nchrome-devtools performance_stop_trace 1 --filePath \"t.json.gz\" # Stop trace and save to a file\n```\n\n## Memory\n\n```bash\nchrome-devtools take_heapsnapshot 1 \"./snap.heapsnapshot\" # Capture a memory heap snapshot\n```\n\n### Memory Debugging (requires `--memoryDebugging=true`)\n\n```bash\nchrome-devtools get_heapsnapshot_summary \"./snap.heapsnapshot\" # Get snapshot summary stats\nchrome-devtools compare_heapsnapshots \"./base.heapsnapshot\" \"./target.heapsnapshot\" # Compare two snapshots\nchrome-devtools get_heapsnapshot_details \"./snap.heapsnapshot\" # Snapshot statistics and per-class aggregates (with class IDs)\nchrome-devtools get_heapsnapshot_class_nodes \"./snap.heapsnapshot\" 18 # Inspect instances of a class (class ID from get_heapsnapshot_details)\nchrome-devtools get_heapsnapshot_dominators \"./snap.heapsnapshot\" 123 # Dominator tree for node\nchrome-devtools get_heapsnapshot_duplicate_strings \"./snap.heapsnapshot\" # Find duplicated strings\nchrome-devtools get_heapsnapshot_edges \"./snap.heapsnapshot\" 123 # Node edges/references\nchrome-devtools get_heapsnapshot_object_details \"./snap.heapsnapshot\" 123 # Object details by node ID\nchrome-devtools get_heapsnapshot_retainers \"./snap.heapsnapshot\" 123 # Retaining objects\nchrome-devtools get_heapsnapshot_retaining_paths \"./snap.heapsnapshot\" 123 # Shortest retaining paths\nchrome-devtools close_heapsnapshot \"./snap.heapsnapshot\" # Free memory from loaded snapshot\n```\n\n## Network\n\n```bash\nchrome-devtools get_network_request 1 # Get the currently selected network request for page 1\nchrome-devtools get_network_request 1 --reqid 1 --requestFilePath \"req.md\" # Get request by id and save to file\nchrome-devtools get_network_request 1 --reqid 1 --responseFilePath \"res.md\" # Save response body to file\nchrome-devtools list_network_requests 1 # List all network requests for page 1\nchrome-devtools list_network_requests 1 --pageSize 50 --pageIdx 0 # List network requests with pagination\nchrome-devtools list_network_requests 1 --resourceTypes fetch # Filter requests by resource type\nchrome-devtools list_network_requests 1 --includePreservedRequests true # Include preserved requests\n```\n\n## Debugging & Inspection\n\n```bash\nchrome-devtools evaluate_script \"() => document.title\" --pageId 1 # Evaluate a JavaScript function on page 1\nchrome-devtools evaluate_script \"(a) => a.innerText\" --pageId 1 --args 1_4 # Evaluate JS with UID arguments on page 1\nchrome-devtools evaluate_script --pageId 1 --sourcePath /path/to/script.js --format script # Evaluate a local classic JavaScript file on page 1\nchrome-devtools get_console_message 1 1 # Gets a console message by its ID\nchrome-devtools get_css_styles 1 \"1_4\" # Get CSS styles with pagination on page 1 (default: 10 rules, pageIdx 0)\nchrome-devtools get_css_styles 1 \"1_4\" --pageSize 20 --pageIdx 1 # Paginate CSS rules with custom page size and custom 0-based page index\nchrome-devtools lighthouse_audit 1 --mode \"navigation\" # Run Lighthouse audit for navigation\nchrome-devtools lighthouse_audit 1 --mode \"snapshot\" --device \"mobile\" # Run Lighthouse audit for a snapshot on mobile\nchrome-devtools lighthouse_audit 1 --outputDirPath ./out # Run Lighthouse audit and save reports\nchrome-devtools list_console_messages 1 # List all console messages\nchrome-devtools list_console_messages 1 --pageSize 20 --pageIdx 1 # List console messages with pagination\nchrome-devtools list_console_messages 1 --types error --types info # Filter console messages by type\nchrome-devtools list_console_messages 1 --includePreservedMessages true # Include preserved messages\nchrome-devtools take_screenshot 1 # Take a screenshot of the page viewport\nchrome-devtools take_screenshot 1 --fullPage true --format \"jpeg\" --quality 80 # Take a full page screenshot as JPEG with quality\nchrome-devtools take_screenshot 1 --uid \"id\" --filePath \"s.png\" # Take a screenshot of an element\nchrome-devtools take_snapshot 1 # Take a text snapshot of the page from the a11y tree\nchrome-devtools take_snapshot 1 --verbose true --filePath \"s.txt\" # Take a verbose snapshot and save to file\nchrome-devtools screencast_start 1 --filePath \"screen.mp4\" # Starts an mp4 screencast recording on page 1\nchrome-devtools screencast_stop 1 # Stops the active screencast on page 1\n```\n\n## Extensions\n\n```bash\nchrome-devtools list_extensions # Lists all the Chrome extensions installed in the browser\nchrome-devtools install_extension \"/path/to/extension\" # Installs a Chrome extension from the given path\nchrome-devtools uninstall_extension \"extension_id\" # Uninstalls a Chrome extension by its ID\nchrome-devtools reload_extension \"extension_id\" # Reloads an unpacked Chrome extension by its ID\nchrome-devtools trigger_extension_action \"extension_id\" # Triggers the default action of an extension by its ID\n```\n\n## Progressive Web Apps (requires `--categoryPwa=true`)\n\n```bash\nchrome-devtools install_pwa \"https://example.com/\" \"https://example.com/\" # Install PWA by manifest ID and install URL\nchrome-devtools launch_pwa \"https://example.com/\" # Launch installed PWA\nchrome-devtools get_os_app_state \"https://example.com/\" # Get OS app installation state\nchrome-devtools uninstall_pwa \"https://example.com/\" # Uninstall PWA and close windows\n```\n\n## Experimental Features\n\nExperimental tools are disabled by default. Enable them with the corresponding flag during `start`.\n\n```bash\nchrome-devtools click_at 1 100 200 # Clicks at the provided coordinates on page 1 (requires --experimentalVision=true)\nchrome-devtools list_webmcp_tools 1 # List all WebMCP tools on page 1 (requires --categoryExperimentalWebmcp=true)\nchrome-devtools execute_webmcp_tool 1 \"tool_name\" --input '{\"arg\":\"val\"}' # Execute a WebMCP tool on page 1 (requires --categoryExperimentalWebmcp=true)\nchrome-devtools list_3p_developer_tools 1 # List third-party developer tools on page 1 (requires --categoryExperimentalThirdParty=true)\nchrome-devtools execute_3p_developer_tool 1 \"tool_name\" --params '{\"arg\":\"val\"}' # Execute third-party developer tool on page 1 (requires --categoryExperimentalThirdParty=true)\n```\n\n## Service Management\n\n```bash\nchrome-devtools start   # Start or restart chrome-devtools-mcp\nchrome-devtools start --headless=false # Start with visible browser window\nchrome-devtools status  # Checks if chrome-devtools-mcp is running\nchrome-devtools stop    # Stop chrome-devtools-mcp if any\n```\n\n<!-- File: skills/chrome-devtools/SKILL.md (Tokens: ~984 | Category: anthropic-skill) -->\n---\nname: chrome-devtools\ndescription: Uses Chrome DevTools via MCP for efficient debugging, troubleshooting and browser automation. Use when debugging web pages, automating browser interactions, analyzing performance, or inspecting network requests. This skill does not apply to `--slim` mode (MCP configuration).\n---\n\n## Core Concepts\n\n**Browser lifecycle**: Browser starts automatically on first tool call using a persistent Chrome profile. Configure via CLI args in the MCP server configuration: `npx chrome-devtools-mcp@latest --help`.\nAddional tooling can be enabled by providing the following flags:\n\n- For extension tooling, use the `--categoryExtensions` flag.\n- For memory tooling, use the `--memoryDebugging` flag.\n\n**Page targeting**: Page-scoped tools require a `pageId` parameter to target a specific page. Use `list_pages` to see available pages and their IDs (e.g. `pageId: 1`), or use the ID returned when creating a page with `new_page`.\nNote: For `evaluate_script`, `pageId` is required when targeting pages. However, when `--categoryExtensions` is enabled, `pageId` is optional so you can pass `serviceWorkerId` instead to evaluate inside an extension background service worker.\n**Element interaction**: Use `take_snapshot` to get page structure with element `uid`s. Each element has a unique `uid` for interaction. If an element isn't found, take a fresh snapshot - the element may have been removed or the page changed.\n\n## Workflow Patterns\n\n### Before interacting with a page\n\n1. Navigate: `navigate_page` or `new_page`\n2. Wait: `wait_for` to ensure content is loaded if you know what you look for.\n3. Snapshot: `take_snapshot` with `pageId` to understand page structure\n4. Interact: Use element `uid`s from snapshot for `click`, `fill`, etc., passing the corresponding `pageId`.\n\n### Efficient data retrieval\n\n- Use `filePath` parameter for large outputs (screenshots, snapshots, traces)\n- Use pagination (`pageIdx`, `pageSize`) and filtering (`types`) to minimize data\n- Set `includeSnapshot: false` on input actions unless you need updated page state\n\n### Tool selection\n\n- **Automation/interaction**: `take_snapshot` (text-based, faster, better for automation)\n- **Visual inspection**: `take_screenshot` (when user needs to see visual state)\n- **CSS & Styling inspection**: Use `get_css_styles` to inspect matched rules, cascade, and CSS variables. Treat the output as authoritative and complete.\n- **Additional details**: Use `evaluate_script` for runtime DOM/JS data not available via `get_css_styles` or `take_snapshot`.\n\n### Parallel execution\n\nYou can send multiple tool calls in parallel, but maintain correct order: navigate → wait → snapshot → interact.\n\n### Testing an extension\n\n> **Before proceeding**: Extension tools (`install_extension`, `list_extensions`, etc.) are only available when the MCP server is started with the `--categoryExtensions` flag. If these tools are not in your tool list, stop and ask the user to update their MCP server configuration:\n>\n> ```json\n> {\n>   \"mcpServers\": {\n>     \"chrome-devtools\": {\n>       \"command\": \"npx\",\n>       \"args\": [\"chrome-devtools-mcp@latest\", \"--categoryExtensions\"]\n>     }\n>   }\n> }\n> ```\n>\n> After updating, the user must restart the MCP server (or their AI client) for the change to take effect.\n\n1. **Install**: Use `install_extension` with the path to the unpacked extension.\n2. **Identify**: Get the extension ID from the response or by calling `list_extensions`.\n3. **Trigger Action**: Use `trigger_extension_action` to open the popup or side panel if applicable.\n4. **Verify Service Worker**: Use `evaluate_script` with `serviceWorkerId` (omitting `pageId` and `args`) to check extension state or trigger background actions. When evaluating in a page, pass `pageId` (omitting `serviceWorkerId`).\n5. **Verify Page Behavior**: Navigate to a page where the extension operates and use `take_snapshot` to check if content scripts injected elements or modified the page correctly.\n\n## Troubleshooting\n\nIf `chrome-devtools-mcp` is insufficient, guide users to use Chrome DevTools UI:\n\n- https://developer.chrome.com/docs/devtools\n- https://developer.chrome.com/docs/devtools/ai-assistance\n\nIf there are errors launching `chrome-devtools-mcp` or Chrome, refer to https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/troubleshooting.md.\n\n<!-- File: skills/cookie-debugging/SKILL.md (Tokens: ~2269 | Category: anthropic-skill) -->\n---\nname: cookie-debugging\ndescription: Uses Chrome DevTools MCP for inspecting, debugging, and testing cookies, session state, authentication issues, and cookie consent compliance. Use when diagnosing 401/403 errors, authentication redirects, session expiration, Cookie/Set-Cookie header issues, cookie banner consent conformance, or third-party cookie/SameSite/Partitioned cookie warnings.\n---\n\n## Core Concepts\n\n**Page Targeting**: Page-scoped tools (`list_network_requests`, `get_network_request`, `list_console_messages`, `evaluate_script`, `navigate_page`, `take_snapshot`, `click`, `lighthouse_audit`, `close_page`, etc.) require a `pageId` parameter. Retrieve available page IDs using `list_pages` or from `new_page`.\n\n### HttpOnly vs Client-Side Storage\n\nCookies marked `HttpOnly` cannot be accessed or modified by client-side JavaScript (`cookieStore` or `document.cookie`). However, the browser **automatically attaches active HttpOnly cookies to outgoing HTTP request headers (`Cookie`)**.\n\n- To inspect current `HttpOnly` values: Look at the `Cookie` request header of any outgoing HTTP request via `get_network_request`.\n- To inspect how cookies were created or configured: Look at the `Set-Cookie` response header of login/auth responses.\n- To inspect non-`HttpOnly` cookies: Use `evaluate_script` with the modern `cookieStore` API (`async () => await cookieStore.getAll()`).\n\n### Session Strategy: Live Tab vs Isolated Context\n\nChoose the right session environment to avoid state contamination (e.g., residual analytics or auth tokens):\n\n| Strategy                            | When to Use                                                                   | Setup / Teardown                                                                                              |\n| :---------------------------------- | :---------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------ |\n| **Live Tab (Active Page)**          | Diagnosing an active user session, live 401/403 error, or current state.      | Operates on the user's existing page; get its `pageId` from `list_pages`.                                     |\n| **Clean-Slate (`isolatedContext`)** | Testing cookie consent banners, first-time visits, or zero-cookie guarantees. | Call `new_page` with a unique `isolatedContext` (e.g. `\"consent-audit-1\"`). When finished, call `close_page`. |\n\n### Client-Side Capabilities & Limitations\n\n| Action                                                           | Client JavaScript (`cookieStore` / `document.cookie`) | DevTools Network & Context Tools                        |\n| :--------------------------------------------------------------- | :---------------------------------------------------- | :------------------------------------------------------ |\n| **Read Non-HttpOnly**                                            | ✅ `async () => await cookieStore.getAll()`           | ✅ `get_network_request` (Request `Cookie`)             |\n| **Read HttpOnly**                                                | ❌ Blocked by browser security                        | ✅ `get_network_request` (Request `Cookie`)             |\n| **Inspect Attributes** (`Domain`, `Path`, `SameSite`, `Expires`) | ✅ `async () => await cookieStore.getAll()`           | ✅ `get_network_request` (Response `Set-Cookie`)        |\n| **Modify / Delete Non-HttpOnly**                                 | ✅ `async () => await cookieStore.set(...)`           | N/A                                                     |\n| **Modify / Delete HttpOnly**                                     | ❌ **Silent failure** in JavaScript                   | ✅ Use `new_page(isolatedContext: ...)` for clean state |\n\n> [!WARNING]\n> Attempting to clear an `HttpOnly` cookie via JavaScript (`cookieStore.delete` or `document.cookie = \"...; max-age=0\"`) will silently fail. To test in an unauthenticated or fresh state, always spawn a new isolated context using `new_page` with `isolatedContext`.\n\n---\n\n## Workflow Patterns\n\n### 1. Diagnosing Authentication Failures & Redirects (401 / 403)\n\nWhen an authenticated page request fails, returns 401/403, or redirects to login:\n\n1. **List Recent Requests**: Call `list_network_requests` with `includePreservedRequests: true`.\n2. **Find the Target Request**: Locate the failing request (401/403) or redirect (302/307).\n3. **Inspect Outgoing `Cookie` Header**: Call `get_network_request` with the `reqid`.\n   - Verify if the `Cookie` header was attached and whether required tokens (e.g. `SESSION_ID`, `auth_token`) were sent.\n4. **Trigger Active Inspection (If no recent request exists)**:\n   - If the cookie was set in a previous session and no network call is listed, trigger a request:\n     - Use `navigate_page` with `type: \"reload\"`, OR\n     - Call `evaluate_script` with `() => fetch(window.location.href)`\n   - Then call `get_network_request` on the new request to inspect the active `Cookie` header.\n5. **Trace the Setting Request**: If the cookie is missing or rejected:\n   - Check earlier login/handshake responses for `Set-Cookie` directives:\n     - **Path mismatch**: e.g., `Path=/api` when the request is to `/`.\n     - **Domain mismatch**: e.g., `Domain=api.example.com` preventing cookies on `sub.example.com`.\n     - **Secure flag on HTTP**: `Secure` cookies are never sent over unencrypted `http://`.\n     - **SameSite blocking**: `SameSite=Strict` cookies are omitted on cross-site navigations.\n     - **Expiration**: Check if `Expires` or `Max-Age` elapsed.\n\n### 2. Cookie Banner & Consent Conformance Testing\n\nTo verify that no non-essential or tracking cookies are set before consent or when declining:\n\n1. **Start Clean**: Open a fresh isolated context with a dedicated name:\n   ```json\n   {\"url\": \"<PAGE_URL>\", \"isolatedContext\": \"consent-test-1\"}\n   ```\n2. **Record Baseline Cookies**: Before interacting with the banner, run `evaluate_script` with `async () => await cookieStore.getAll()`.\n3. **Inspect Premature Network Requests & Issues**:\n   - Call `list_network_requests` to ensure no third-party tracking beacons fired before consent.\n   - Call `list_console_messages` with `types: [\"issue\"]` to check for tracking warnings.\n4. **Interact with Consent Banner**:\n   - Capture snapshot with `take_snapshot` to locate the \"Decline\" or \"Reject All\" button `uid`.\n   - Click the button with `click`.\n5. **Verify Cookie Difference**:\n   - Run `evaluate_script` with `async () => await cookieStore.getAll()` after clicking to assert that only strictly necessary or consent-state cookies exist.\n6. **Test Consent Revocation (Lifecycle Audit)**:\n   - When auditing consent withdrawal or preference changes:\n     - Locate and click the \"Cookie Settings\", \"Manage Preferences\", or footer privacy trigger (`take_snapshot` $\\rightarrow$ `click`).\n     - Deselect non-essential categories or click \"Revoke All\" / \"Save Preferences\".\n     - Re-query `cookieStore.getAll()` to verify previously accepted non-essential cookies were cleared or expired.\n     - Call `list_network_requests` on subsequent actions to ensure tracking beacons are no longer fired.\n7. **Teardown Context**: Call `close_page` when the audit is complete to prevent leftover cookies from affecting subsequent tasks.\n\n### 3. Auditing Cookie Security, SameSite & CHIPS (Partitioned Cookies)\n\n1. **Fast-Track: Native DevTools Issues (Recommended)**:\n   - Call `list_console_messages` with:\n     ```json\n     {\n       \"pageId\": 1,\n       \"types\": [\"issue\"],\n       \"includePreservedMessages\": true\n     }\n     ```\n   - Check for `CookieIssue` entries, such as:\n     - `SameSiteNoneInsecure`: `SameSite=None` without `Secure`.\n     - `ThirdPartyCookiePhaseout`: Third-party cookie blocked or restricted.\n     - `SchemefulSameSite`: Cross-scheme cookie issues.\n     - `PartitionedCookies`: Invalid CHIPS partitioning attributes.\n2. **Deep Audit: Lighthouse Third-Party Cookies**:\n   - Run `lighthouse_audit` with `mode: \"navigation\"` (omit `outputDirPath` to use the default temporary files, or specify a directory within the workspace).\n   - **Extract the specific cookie audit** from the returned `report.json` path without loading the full report into context:\n     ```bash\n     node -e \"const r=require('<path/to/report.json>'); const a=r.audits['third-party-cookies']; console.log(JSON.stringify({score: a?.score, displayValue: a?.displayValue, items: a?.details?.items}))\"\n     ```\n\n### 4. Client-Side Cookie Inspection & Manipulation\n\nFor client-accessible, non-`HttpOnly` cookies (e.g., UI preferences, non-sensitive feature flags):\n\n1. **Read Cookies & Attributes**:\n   - Use the modern asynchronous Cookie Store API:\n     ```js\n     async () => await cookieStore.getAll();\n     ```\n   - _Fallback for insecure HTTP origins_: `() => document.cookie`.\n2. **Set / Modify Cookie**:\n   - Set client cookie via `cookieStore`:\n     ```js\n     async () =>\n       await cookieStore.set({\n         name: 'theme',\n         value: 'dark',\n         expires: Date.now() + 86400000,\n         sameSite: 'lax',\n       });\n     ```\n3. **Delete Cookie**:\n   - Clear client cookie:\n     ```js\n     async () => await cookieStore.delete('theme');\n     ```\n\n---\n\n## Troubleshooting\n\n- **`cookieStore` is undefined**: `cookieStore` requires a Secure Context (`https://`, `localhost`, or `127.0.0.1`). On non-secure HTTP origins, use `() => document.cookie` or test over HTTPS.\n- **`evaluate_script` returns empty / unresolved Promise**: `cookieStore` methods are asynchronous. Always wrap calls with `async () => await cookieStore.getAll()`.\n- **Cookie not visible in JavaScript**: The cookie is marked `HttpOnly`. Trigger a network request and call `get_network_request` to view it in the `Cookie` request header.\n- **JavaScript deletion did not remove cookie**: The cookie is `HttpOnly` or requires matching `Path` and `Domain` parameters. Use a fresh `isolatedContext` with `new_page` for a clean slate.\n- **Cookie set in response but not sent in requests**:\n  - Verify if page is `http://` while cookie specifies `Secure`.\n  - Check if `Domain` restricts subdomains.\n  - Check `list_console_messages(types: [\"issue\"])` for browser rejection reasons.\n- **Residual cookies contaminating audits**: Always use `new_page` with a unique `isolatedContext` when running compliance tests, and call `close_page` when done.\n\n<!-- File: skills/debug-optimize-lcp/references/elements-and-size.md (Tokens: ~284 | Category: anthropic-skill) -->\n# Elements and Size for LCP\n\n## What Elements are Considered?\n\nThe types of elements considered for Largest Contentful Paint (LCP) are:\n\n- **`<img>` elements**: The first frame presentation time is used for animated content like GIFs.\n- **`<image>` elements** inside an `<svg>` element.\n- **`<video>` elements**: The poster image load time or first frame presentation time, whichever is earlier.\n- **Background images**: Elements with a background image loaded using `url()`.\n- **Block-level elements**: Containing text nodes or other inline-level text element children.\n\n## Heuristics to Exclude Non-Contentful Elements\n\nChromium-based browsers use heuristics to exclude:\n\n- Elements with **opacity of 0**.\n- Elements that **cover the full viewport** (likely background).\n- **Placeholder images** or low-entropy images.\n\n## How is an Element's Size Determined?\n\n- **Visible Area**: Typically the size visible within the viewport. Extending outside, clipped, or overflow portions don't count.\n- **Image Elements**: Either the visible size or the intrinsic size, whichever is smaller.\n- **Text Elements**: The smallest rectangle containing all text nodes.\n- **Exclusions**: Margin, padding, and borders are not considered toward the size.\n- **Containment**: Every text node belongs to its closest block-level ancestor element.\n\n<!-- File: skills/debug-optimize-lcp/references/lcp-breakdown.md (Tokens: ~468 | Category: anthropic-skill) -->\n# Largest Contentful Paint (LCP) Breakdown\n\nLCP measures the time from when the user initiates loading the page until the largest image or text block is rendered within the viewport. To provide a good user experience, sites should strive to have an LCP of 2.5 seconds or less for at least 75% of page visits.\n\n## The Four Subparts of LCP\n\nEvery page's LCP consists of these four subcategories. There's no gap or overlap between them, and they add up to the full LCP time.\n\n| LCP subpart                   | % of LCP (Optimal) | Description                                                                                                                                                              |\n| ----------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| **Time to First Byte (TTFB)** | ~40%               | The time from when the user initiates loading the page until the browser receives the first byte of the HTML document response.                                          |\n| **Resource load delay**       | <10%               | The time between TTFB and when the browser starts loading the LCP resource. If the LCP element doesn't require a resource load (e.g., system font text), this time is 0. |\n| **Resource load duration**    | ~40%               | The duration of time it takes to load the LCP resource itself. If the LCP element doesn't require a resource load, this time is 0.                                       |\n| **Element render delay**      | <10%               | The time between when the LCP resource finishes loading and the LCP element rendering fully.                                                                             |\n\n## Why the Breakdown Matters\n\nOptimizing for LCP requires identifying which of these subparts is the bottleneck:\n\n- **Large delta between TTFB and FCP**: Indicates the browser needs to download a lot of render-blocking assets or complete a lot of work (e.g., client-side rendering).\n- **Large delta between FCP and LCP**: Indicates the LCP resource is not immediately available for the browser to prioritize or the browser is completing other work before it can display the LCP content.\n- **Large resource load delay**: Indicates the resource is not discoverable early or is deprioritized.\n- **Large element render delay**: Indicates rendering is blocked by stylesheets, scripts, or long tasks.\n\n<!-- File: skills/debug-optimize-lcp/references/lcp-snippets.md (Tokens: ~550 | Category: anthropic-skill) -->\n# LCP Debugging Snippets\n\nUse these JavaScript snippets with the `evaluate_script` tool to extract deep insights from the page.\n\n## 1. Identify LCP Element\n\nUse this snippet to identify the LCP element and get raw timing data from the Performance API.\n\n```javascript\nasync () => {\n  return await new Promise(resolve => {\n    new PerformanceObserver(list => {\n      const entries = list.getEntries();\n      const last = entries[entries.length - 1];\n      resolve({\n        element: last.element?.tagName,\n        id: last.element?.id,\n        className: last.element?.className,\n        url: last.url,\n        startTime: last.startTime,\n        renderTime: last.renderTime,\n        loadTime: last.loadTime,\n        size: last.size,\n      });\n    }).observe({type: 'largest-contentful-paint', buffered: true});\n  });\n};\n```\n\n## 2. Audit Common Issues\n\nUse this snippet to check for common DOM-based LCP issues (lazy loading, priority).\n\n```javascript\n() => {\n  const issues = [];\n\n  // Check for lazy-loaded images in viewport\n  document.querySelectorAll('img[loading=\"lazy\"]').forEach(img => {\n    const rect = img.getBoundingClientRect();\n    if (rect.top < window.innerHeight) {\n      issues.push({\n        issue: 'lazy-loaded image in viewport',\n        element: img.outerHTML.substring(0, 200),\n        fix: 'Remove loading=\"lazy\" from this image — it is in the initial viewport and may be the LCP element',\n      });\n    }\n  });\n\n  // Check for LCP-candidate images missing fetchpriority\n  document.querySelectorAll('img:not([fetchpriority])').forEach(img => {\n    const rect = img.getBoundingClientRect();\n    if (rect.top < window.innerHeight && rect.width * rect.height > 50000) {\n      issues.push({\n        issue: 'large viewport image without fetchpriority',\n        element: img.outerHTML.substring(0, 200),\n        fix: 'Add fetchpriority=\"high\" to this image — it is large and visible in the initial viewport',\n      });\n    }\n  });\n\n  // Check for render-blocking scripts in head\n  document\n    .querySelectorAll(\n      'head script:not([async]):not([defer]):not([type=\"module\"])',\n    )\n    .forEach(script => {\n      if (script.src) {\n        issues.push({\n          issue: 'render-blocking script in head',\n          element: script.outerHTML.substring(0, 200),\n          fix: 'Add async or defer attribute, or move to end of body',\n        });\n      }\n    });\n\n  return {issueCount: issues.length, issues};\n};\n```\n\n<!-- File: skills/debug-optimize-lcp/references/optimization-strategies.md (Tokens: ~479 | Category: anthropic-skill) -->\n# LCP Optimization Strategies\n\n## 1. Eliminate Resource Load Delay\n\n**Goal**: Ensure the LCP resource starts loading as early as possible.\n\n- **Early Discovery**: Ensure the LCP resource is discoverable in the initial HTML document response (not dynamically added by JS or hidden in `data-src`).\n- **Preload**: Use `<link rel=\"preload\">` with `fetchpriority=\"high\"` for critical images or fonts.\n- **Avoid Lazy Loading**: Never set `loading=\"lazy\"` on the LCP image.\n- **Fetch Priority**: Use `fetchpriority=\"high\"` on the `<img>` tag.\n- **Same Origin**: Host critical resources on the same origin or use `<link rel=\"preconnect\">`.\n\n## 2. Eliminate Element Render Delay\n\n**Goal**: Ensure the LCP element can render immediately after its resource has finished loading.\n\n- **Minimize Render-Blocking CSS**: Inline critical CSS and defer non-critical CSS. Ensure the stylesheet is smaller than the LCP resource.\n- **Minimize Render-Blocking JS**: Avoid synchronous scripts in the `<head>`. Inline very small scripts.\n- **Server-Side Rendering (SSR)**: Deliver the full HTML markup from the server so image resources are discoverable immediately.\n- **Break Up Long Tasks**: Prevent large JavaScript tasks from blocking the main thread during rendering.\n\n## 3. Reduce Resource Load Duration\n\n**Goal**: Reduce the time spent transferring the bytes of the resource.\n\n- **Optimize Resource Size**: Serve optimal image sizes, use modern formats (AVIF, WebP), and compress images/fonts.\n- **Geographic Proximity (CDN)**: Use a Content Delivery Network to get servers closer to users.\n- **Reduce Contention**: Use `fetchpriority=\"high\"` to prevent lower-priority resources from competing for bandwidth.\n- **Caching**: Use efficient `Cache-Control` policies.\n\n## 4. Reduce Time to First Byte (TTFB)\n\n**Goal**: Deliver the initial HTML as quickly as possible.\n\n- **Minimize Redirects**: Avoid multiple redirects from advertisements or shortened links.\n- **CDN Caching**: Cache static HTML documents at the edge.\n- **Edge Computing**: Move dynamic logic to the edge to avoid trips to the origin server.\n- **Back/Forward Cache**: Ensure pages are eligible for bfcache.\n\n<!-- File: skills/debug-optimize-lcp/SKILL.md (Tokens: ~1554 | Category: anthropic-skill) -->\n---\nname: debug-optimize-lcp\ndescription: Guides debugging and optimizing Largest Contentful Paint (LCP) using Chrome DevTools MCP tools. Use this skill whenever the user asks about LCP performance, slow page loads, Core Web Vitals optimization, or wants to understand why their page's main content takes too long to appear. Also use when the user mentions \"largest contentful paint\", \"page load speed\", \"CWV\", or wants to improve how fast their hero image or main content renders.\n---\n\n## What is LCP and why it matters\n\nLargest Contentful Paint (LCP) measures how quickly a page's main content becomes visible. It's the time from navigation start until the largest image or text block renders in the viewport.\n\n- **Good**: 2.5 seconds or less\n- **Needs improvement**: 2.5–4.0 seconds\n- **Poor**: greater than 4.0 seconds\n\nLCP is a Core Web Vital that directly affects user experience and search ranking. On 73% of mobile pages, the LCP element is an image.\n\n## LCP Subparts Breakdown\n\nEvery page's LCP breaks down into four sequential subparts with no gaps or overlaps. Understanding which subpart is the bottleneck is the key to effective optimization.\n\n| Subpart                       | Ideal % of LCP | What it measures                               |\n| ----------------------------- | -------------- | ---------------------------------------------- |\n| **Time to First Byte (TTFB)** | ~40%           | Navigation start → first byte of HTML received |\n| **Resource load delay**       | <10%           | TTFB → browser starts loading the LCP resource |\n| **Resource load duration**    | ~40%           | Time to download the LCP resource              |\n| **Element render delay**      | <10%           | LCP resource downloaded → LCP element rendered |\n\nThe \"delay\" subparts should be as close to zero as possible. If either delay subpart is large relative to the total LCP, that's the first place to optimize.\n\n**Common Pitfall**: Optimizing one subpart (like compressing an image to reduce load duration) without checking others. If render delay is the real bottleneck, a smaller image won't help — the saved time just shifts to render delay.\n\n## Debugging Workflow\n\nFollow these steps in order. Each step builds on the previous one.\n\n### Step 1: Record a Performance Trace\n\nNavigate to the page, then record a trace with reload to capture the full page load including LCP:\n\n1. `navigate_page` with `pageId` to the target URL.\n2. `performance_start_trace` with `pageId`, `reload: true` and `autoStop: true`.\n\nThe trace results will include LCP timing and available insight sets. Note the insight set IDs from the output — you'll need them in the next step.\n\n### Step 2: Analyze LCP Insights\n\nUse `performance_analyze_insight` to drill into LCP-specific insights. Look for these insight names in the trace results:\n\n- **LCPBreakdown** — Shows the four LCP subparts with timing for each.\n- **DocumentLatency** — Server response time issues affecting TTFB.\n- **RenderBlocking** — Resources blocking the LCP element from rendering.\n- **LCPDiscovery** — Whether the LCP resource was discoverable early.\n\nCall `performance_analyze_insight` with `pageId`, the insight set ID, and the insight name from the trace results.\n\n### Step 3: Identify the LCP Element\n\nUse `evaluate_script` (with `pageId`) and the **\"Identify LCP Element\" snippet** found in [references/lcp-snippets.md](references/lcp-snippets.md) to reveal the LCP element's tag, resource URL, and raw timing data.\n\nThe `url` field tells you what resource to look for in the network waterfall. If `url` is empty, the LCP element is text-based (no resource to load).\n\n### Step 4: Check the Network Waterfall\n\nUse `list_network_requests` to see when the LCP resource loaded relative to other resources:\n\n- Call `list_network_requests` with `pageId` filtered by `resourceTypes: [\"image\", \"font\"]` (adjust based on Step 3).\n- Then use `get_network_request` with `pageId` and the LCP resource's request ID for full details.\n\n**Key Checks:**\n\n- **Start Time**: Compare against the HTML document and the first resource. If the LCP resource starts much later than the first resource, there's resource load delay to eliminate.\n- **Duration**: A large resource load duration suggests the file is too big or the server is slow.\n\n### Step 5: Inspect HTML for Common Issues\n\nUse `evaluate_script` (with `pageId`) and the **\"Audit Common Issues\" snippet** found in [references/lcp-snippets.md](references/lcp-snippets.md) to check for lazy-loaded images in the viewport, missing fetchpriority, and render-blocking scripts.\n\n## Optimization Strategies\n\nAfter identifying the bottleneck subpart, apply these prioritized fixes.\n\n### 1. Eliminate Resource Load Delay (target: <10%)\n\nThe most common bottleneck. The LCP resource should start loading immediately.\n\n- **Root Cause**: LCP image loaded via JS/CSS, `data-src` usage, or `loading=\"lazy\"`.\n- **Fix**: Use standard `<img>` with `src`. **Never** lazy-load the LCP image.\n- **Fix**: Add `<link rel=\"preload\" fetchpriority=\"high\">` if the image isn't discoverable in HTML.\n- **Fix**: Add `fetchpriority=\"high\"` to the LCP `<img>` tag.\n\n### 2. Eliminate Element Render Delay (target: <10%)\n\nThe element should render immediately after loading.\n\n- **Root Cause**: Large stylesheets, synchronous scripts in `<head>`, or main thread blocking.\n- **Fix**: Inline critical CSS, defer non-critical CSS/JS.\n- **Fix**: Break up long tasks blocking the main thread.\n- **Fix**: Use Server-Side Rendering (SSR) so the element exists in initial HTML.\n\n### 3. Reduce Resource Load Duration (target: ~40%)\n\nMake the resource smaller or faster to deliver.\n\n- **Fix**: Use modern formats (WebP, AVIF) and responsive images (`srcset`).\n- **Fix**: Serve from a CDN.\n- **Fix**: Set `Cache-Control` headers.\n- **Fix**: Use `font-display: swap` if LCP is text blocked by a web font.\n\n### 4. Reduce TTFB (target: ~40%)\n\nThe HTML document itself takes too long to arrive.\n\n- **Fix**: Minimize redirects and optimize server response time.\n- **Fix**: Cache HTML at the edge (CDN).\n- **Fix**: Ensure pages are eligible for back/forward cache (bfcache).\n\n## Verifying Fixes & Emulation\n\n- **Verification**: Re-run the trace (`performance_start_trace` with `pageId` and `reload: true`) and compare the new subpart breakdown. The bottleneck should shrink.\n- **Emulation**: Lab measurements differ from real-world experience. Use `emulate` to test under constraints:\n  - `emulate` with `pageId`, `networkConditions: \"Fast 3G\"` and `cpuThrottlingRate: 4`.\n  - This surfaces issues visible only on slower connections/devices.\n\n<!-- File: skills/memory-leak-debugging/references/common-leaks.md (Tokens: ~392 | Category: anthropic-skill) -->\n# Common Memory Leaks\n\nWhen analyzing retaining paths, dominator chains, or class diffs with Chrome DevTools MCP memory tools, look for these common patterns in the codebase:\n\n## 1. Uncleared Event Listeners\n\nEvent listeners attached to global objects (like `window` or `document`) or long-living objects prevent garbage collection of the objects referenced in their callbacks.\n\n**Fix:** Always call `removeEventListener` when a component unmounts or the listener is no longer needed.\n\n## 2. Detached DOM Nodes\n\nA DOM node is removed from the document tree but is still referenced by a JavaScript variable. While detachedness is a good signal for a memory leak, it's not always a bug. For example, websites sometimes intentionally cache detached navigation trees.\n\n**Fix:** Signal the detached nodes to the user first. **Ask the user first** before nulling the references or changing the code, as the detached nodes might be part of an intentional cache. If confirmed as a leak, ensure variables holding DOM references are set to `null` when the node is removed, or limit their scope.\n\n## 3. Unintentional Global Variables\n\nVariables declared without `var`, `let`, or `const` (in non-strict mode) or explicitly attached to `window` remain in memory forever.\n\n**Fix:** Use strict mode, properly declare variables, and avoid global state.\n\n## 4. Closures\n\nClosures can unintentionally keep references to large objects in their outer scope.\n\n**Fix:** Nullify large objects when they are no longer needed, or refactor the closure to not capture unnecessary variables.\n\n## 5. Unbounded Caches or Arrays\n\nData structures used for caching (like objects, Arrays, or Maps) that grow without limits.\n\n**Fix:** Implement caching limits, use LRU caches, or use `WeakMap`/`WeakSet` for data associated with object lifecycles.\n\n<!-- File: skills/memory-leak-debugging/SKILL.md (Tokens: ~937 | Category: anthropic-skill) -->\n---\nname: memory-leak-debugging\ndescription: Diagnoses and resolves memory leaks in JavaScript/Node.js applications. Use when a user reports high memory usage, OOM errors, or wants to capture, compare, or inspect heap snapshots with Chrome DevTools MCP memory tools.\n---\n\n# Memory Leak Debugging\n\nThis skill provides expert guidance and workflows for finding, diagnosing, and fixing memory leaks in JavaScript and Node.js applications using Chrome DevTools MCP tools.\n\n## Prerequisites\n\nAdvanced memory debugging tools (`compare_heapsnapshots`, `get_heapsnapshot_details`, etc.) are only available when the server is started with the `--memoryDebugging` flag. First check if these tools are available; if not, try to read the MCP configuration file to check if `--memoryDebugging` is enabled.\n\n## Core Principles\n\n- **Prefer MCP memory tools:** Do NOT attempt to read raw `.heapsnapshot` files directly, as they are extremely large and will consume too many tokens. Use the Chrome DevTools MCP heap snapshot tools to summarize, compare, and inspect snapshots.\n- **Isolate the Leak:** Determine if the leak is in the browser (client-side) or Node.js (server-side).\n- **Common Culprits:** Look for detached DOM nodes, unhandled closures, global variables, event listeners not being removed, and caches growing unbounded. _Note: Detached DOM nodes are sometimes intentional caches; always ask the user before nulling them._\n- **Close Loaded Snapshots:** Heap snapshots can be large. After completing an investigation, use `close_heapsnapshot` for each loaded snapshot to release memory held by the MCP server.\n\n## Workflows\n\n### 1. Capturing Snapshots\n\nWhen investigating a frontend web application memory leak, utilize the `chrome-devtools-mcp` tools to interact with the application and take snapshots.\n\n- Use page-scoped tools like `click`, `navigate_page`, `fill`, etc. (specifying `pageId`) to manipulate the page into the desired state.\n- Revert the page back to the original state after interactions to see if memory is released.\n- Repeat the same user interactions 10 times to amplify the leak.\n- Use `take_heapsnapshot` (with `pageId`) to save `.heapsnapshot` files to disk at baseline, target (after actions), and final (after reverting actions) states.\n\n### 2. Comparing Snapshots\n\nOnce you have generated `.heapsnapshot` files using `take_heapsnapshot`, compare them with Chrome DevTools MCP memory tools.\n\n- Start with `get_heapsnapshot_summary` for each snapshot to confirm that the files load and to compare high-level totals.\n- Use `compare_heapsnapshots` to compare baseline and target snapshots. Start without `classIndex` for the summary diff, then request detailed class diffs only for suspicious growth by specifying `classIndex`.\n- Use the summary output from `compare_heapsnapshots` before drilling into specific node IDs.\n\n### 3. Inspecting Retainers and Dominator Chains\n\nWhen a class or object type grows unexpectedly, inspect the retaining chain and dominators with the MCP tools before changing code.\n\n- Use `get_heapsnapshot_class_nodes` to list instances of the suspicious class.\n- Use `get_heapsnapshot_retainers`, `get_heapsnapshot_retaining_paths`, `get_heapsnapshot_dominators`, and `get_heapsnapshot_edges` to understand why representative nodes are still reachable.\n- Use `get_heapsnapshot_object_details` with a specific `nodeId` to retrieve detailed object metadata (size, type, distance, and DOM detachedness).\n- Use `get_heapsnapshot_duplicate_strings` when string growth dominates the diff.\n- Read [references/common-leaks.md](references/common-leaks.md) for examples of common memory leaks and how to fix them after the retaining path points at application code.\n\n### 4. Advanced Analysis and Categorized Filters\n\nUse built-in MCP memory tools and filters to pinpoint specific leak categories directly without external tools.\n\n- Use `get_heapsnapshot_details` or `get_heapsnapshot_class_nodes` with `filterName` to target common leak causes:\n  - `objectsRetainedByDetachedDomNodes`: Identifies detached DOM elements retained in memory.\n  - `objectsRetainedByEventHandlers`: Identifies objects kept alive by unremoved event listeners.\n  - `objectsRetainedByContexts`: Identifies objects trapped in closures or execution contexts.\n  - `objectsRetainedByConsole`: Identifies objects retained by console logging.\n\n<!-- File: skills/troubleshooting/SKILL.md (Tokens: ~1515 | Category: anthropic-skill) -->\n---\nname: troubleshooting\ndescription: Uses Chrome DevTools MCP and documentation to troubleshoot connection and target issues. Trigger this skill when list_pages, new_page, or navigate_page fail, or when the server initialization fails.\n---\n\n## Troubleshooting Wizard\n\nYou are acting as a troubleshooting wizard to help the user configure and fix their Chrome DevTools MCP server setup. When this skill is triggered (e.g., because `list_pages`, `new_page`, or `navigate_page` failed, or the server wouldn't start), follow this step-by-step diagnostic process:\n\n### Step 1: Find and Read Configuration\n\nYour first action should be to locate and read the MCP configuration file. Search for the following files in the user's workspace: `.mcp.json`, `.vscode/mcp.json`, `gemini-extension.json`, or `.gemini/settings.json` (or user-scoped files such as `~/.claude.json` if accessible).\n\nIf you find a configuration file, read and interpret it to identify potential issues such as:\n\n- Incorrect arguments or flags.\n- Missing environment variables.\n- Usage of `--autoConnect` in incompatible environments.\n\nIf you cannot find any of these files, only then should you ask the user to provide their configuration file content.\n\n### Step 2: Triage Common Connection Errors\n\nBefore reading documentation or suggesting configuration changes, check if the error message matches one of the following common patterns.\n\n#### Error: `Could not find DevToolsActivePort`\n\nThis error is highly specific to the `--autoConnect` feature. It means the MCP server cannot find the file created by a running, debuggable Chrome instance. This is not a generic connection failure.\n\nYour primary goal is to guide the user to ensure Chrome is running and properly configured. Do not immediately suggest switching to `--browserUrl`. Follow this exact sequence:\n\n1. **Ask the user to confirm that the correct Chrome version** (e.g., \"Chrome Canary\" if the error mentions it) is currently running.\n2. **If the user confirms it is running, instruct them to enable remote debugging.** Be very specific about the URL and the action: \"Please open a new tab in Chrome, navigate to `chrome://inspect/#remote-debugging`, and make sure the 'Enable remote debugging' checkbox is checked.\"\n3. **Once the user confirms both steps, your only next action should be to call the `list_pages` tool.** This is the simplest and safest way to verify if the connection is now successful. Do not retry the original, more complex command yet.\n4. **If `list_pages` succeeds, the problem is resolved.** If it still fails with the same error, then you can proceed to the more advanced steps like suggesting `--browserUrl` or checking for sandboxing issues.\n\n#### Symptom: Server starts but creates a new empty profile\n\nIf the server starts successfully but `list_pages` returns an empty list or creates a new profile instead of connecting to the existing Chrome instance, check for typos in the arguments.\n\n- **Check for flag typos:** For example, `--autoBronnect` instead of `--autoConnect`.\n- **Verify the configuration:** Ensure the arguments match the expected flags exactly.\n\n#### Symptom: Missing Tools / Only a few tools available\n\nIf the server starts successfully but only a limited subset of tools (like `list_pages`, `list_console_messages`, `get_console_message`, `list_network_requests`) are available, this is likely because the MCP client is enforcing a **read-only mode**.\n\nAll tools in `chrome-devtools-mcp` are annotated with `readOnlyHint: true` (for safe, non-modifying tools) or `readOnlyHint: false` (for tools that modify browser state, like `emulate`, `click`, `navigate_page`). To access the full suite of tools, the user must disable read-only mode in their MCP client (e.g., by exiting \"Plan Mode\" in Gemini CLI or adjusting their client's tool safety settings).\n\n#### Symptom: Extension tools are missing or extensions fail to load\n\nIf the tools related to extensions (like `install_extension`) are not available, or if the extensions you load are not functioning:\n\n1. **Check for the `--categoryExtensions` flag**: Ensure this flag is passed in the MCP server configuration to enable the extension category tools.\n\n#### Other Common Errors\n\nIdentify other error messages from the failed tool call or the MCP initialization logs:\n\n- `Target closed`\n- \"Tool not found\" (check if they are using `--slim` which only enables navigation, script evaluation and screenshot tools).\n- Missing `pageId`: Page-scoped tools require a `pageId` argument. Call `list_pages` to find active page IDs.\n- `ProtocolError: Network.enable timed out` or `The socket connection was closed unexpectedly`\n- `Error [ERR_MODULE_NOT_FOUND]: Cannot find module`\n- Any sandboxing or host validation errors.\n\n### Step 3: Read Known Issues\n\nRead the contents of https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/troubleshooting.md to map the error to a known issue. Pay close attention to:\n\n- Sandboxing restrictions (macOS Seatbelt, Linux containers).\n- WSL requirements.\n- `--autoConnect` handshakes, timeouts, and requirements (requires **running** Chrome 144+).\n\n### Step 4: Formulate a Configuration\n\nBased on the exact error and the user's environment (OS, MCP client), formulate the correct MCP configuration snippet. Check if they need to:\n\n- Pass `--browser-url=http://127.0.0.1:9222` instead of `--autoConnect` (e.g. if they are in a sandboxed environment like Claude Desktop).\n- Enable remote debugging in Chrome (`chrome://inspect/#remote-debugging`) and accept the connection prompt. **Ask the user to verify this is enabled if using `--autoConnect`.**\n- Add `--logFile <absolute_path_to_log_file>` to capture debug logs for analysis.\n- Increase `startup_timeout_ms` (e.g. to 20000) if using Codex on Windows.\n\n_If you are unsure of the user's configuration, ask the user to provide their current MCP server JSON configuration._\n\n### Step 5: Run Diagnostic Commands\n\nIf the issue is still unclear, run diagnostic commands to test the server directly:\n\n- Run `npx chrome-devtools-mcp@latest --help` to verify the installation and Node.js environment.\n- If you need more information, run `NODE_DEBUG=* npx chrome-devtools-mcp@latest --logFile=/tmp/cdm-test.log` to capture verbose logs. Analyze the output for errors.\n\n### Step 6: Check GitHub for Existing Issues\n\nIf https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/troubleshooting.md does not cover the specific error, check if the `gh` (GitHub CLI) tool is available in the environment. If so, search the GitHub repository for similar issues:\n`gh issue list --repo ChromeDevTools/chrome-devtools-mcp --search \"<error snippet>\" --state all`\n\nAlternatively, you can recommend that the user checks https://github.com/ChromeDevTools/chrome-devtools-mcp/issues and https://github.com/ChromeDevTools/chrome-devtools-mcp/discussions for help.\n\n<!-- File: .claude-plugin/marketplace.json (Tokens: ~145 | Category: marketplace) -->\n{\n  \"name\": \"chrome-devtools-plugins\",\n  \"version\": \"1.12.0\",\n  \"description\": \"Bundled plugins for actuating and debugging the Chrome browser.\",\n  \"repository\": \"https://github.com/ChromeDevTools/chrome-devtools-mcp\",\n  \"owner\": {\n    \"name\": \"Chrome DevTools Team\",\n    \"email\": \"devtools-dev@chromium.org\"\n  },\n  \"plugins\": [\n    {\n      \"name\": \"chrome-devtools-mcp\",\n      \"source\": \"./\",\n      \"description\": \"Reliable automation, in-depth debugging, and performance analysis in Chrome using Chrome DevTools and Puppeteer\"\n    }\n  ]\n}\n\n<!-- File: .claude-plugin/plugin.json (Tokens: ~97 | Category: plugin-manifest) -->\n{\n  \"name\": \"chrome-devtools-mcp\",\n  \"version\": \"1.12.0\",\n  \"description\": \"Reliable automation, in-depth debugging, and performance analysis in Chrome using Chrome DevTools and Puppeteer\",\n  \"mcpServers\": {\n    \"chrome-devtools\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"chrome-devtools-mcp@1.12.0\"\n      ]\n    }\n  }\n}\n\n<!-- File: .cursor-plugin/plugin.json (Tokens: ~204 | Category: plugin-manifest) -->\n{\n  \"name\": \"devtools-for-agents\",\n  \"description\": \"Help your agent build, debug, and verify your code correctly. With Chrome DevTools for agents, your AI agent can interact with the Chrome browser to test code, emulate users, and catch bugs using Chrome DevTools’ capabilities before shipping.\",\n  \"version\": \"1.12.0\",\n  \"author\": {\n    \"name\": \"Google Chrome\"\n  },\n  \"logo\": \"https://github.com/ChromeDevTools/devtools-logo/blob/master/logos/svg/chrome-devtools-square-responsive.svg\",\n  \"homepage\": \"https://developer.chrome.com/docs/devtools/agents\",\n  \"repository\": \"https://github.com/ChromeDevTools/chrome-devtools-mcp\",\n  \"mcpServers\": {\n    \"chrome-devtools\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"chrome-devtools-mcp@1.12.0\"\n      ]\n    }\n  }\n}\n\n<!-- File: .github/plugin/plugin.json (Tokens: ~97 | Category: plugin-manifest) -->\n{\n  \"name\": \"chrome-devtools-mcp\",\n  \"version\": \"1.12.0\",\n  \"description\": \"Reliable automation, in-depth debugging, and performance analysis in Chrome using Chrome DevTools and Puppeteer\",\n  \"mcpServers\": {\n    \"chrome-devtools\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"chrome-devtools-mcp@1.12.0\"\n      ]\n    }\n  }\n}\n\n<!-- File: .kimi-code-plugin/plugin.json (Tokens: ~202 | Category: plugin-manifest) -->\n{\n  \"name\": \"chrome-devtools\",\n  \"description\": \"Help your agent build, debug, and verify your code correctly. With Chrome DevTools for agents, your AI agent can interact with the Chrome browser to test code, emulate users, and catch bugs using Chrome DevTools’ capabilities before shipping.\",\n  \"version\": \"1.12.0\",\n  \"author\": {\n    \"name\": \"Google Chrome\"\n  },\n  \"logo\": \"https://github.com/ChromeDevTools/devtools-logo/blob/master/logos/svg/chrome-devtools-square-responsive.svg\",\n  \"homepage\": \"https://developer.chrome.com/docs/devtools/agents\",\n  \"repository\": \"https://github.com/ChromeDevTools/chrome-devtools-mcp\",\n  \"mcpServers\": {\n    \"chrome-devtools\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"chrome-devtools-mcp@1.12.0\"\n      ]\n    }\n  }\n}\n\n<!-- File: plugin.json (Tokens: ~165 | Category: plugin-manifest) -->\n{\n  \"$schema\": \"https://agent-plugins.org/schemas/1.0.0/plugin.schema.json\",\n  \"name\": \"chrome-devtools\",\n  \"version\": \"1.12.0\",\n  \"description\": \"Reliable Chrome automation, debugging, and performance analysis for AI agents.\",\n  \"author\": {\n    \"name\": \"Google Chrome\",\n    \"url\": \"https://developer.chrome.com/\"\n  },\n  \"homepage\": \"https://developer.chrome.com/docs/devtools/agents\",\n  \"repository\": \"https://github.com/ChromeDevTools/chrome-devtools-mcp\",\n  \"license\": \"Apache-2.0\",\n  \"keywords\": [\n    \"browser\",\n    \"chrome\",\n    \"debugging\",\n    \"mcp\",\n    \"performance\"\n  ]\n}\n</yakaai_skills>"}