@mcp-b/chrome-devtools-mcp 1.7.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/README.md +120 -10
  2. package/build/src/McpContext.js +50 -3
  3. package/build/src/browser.js +62 -6
  4. package/build/src/cli.js +6 -1
  5. package/build/src/formatters/IssueFormatter.js +190 -0
  6. package/build/src/main.js +83 -2
  7. package/build/src/telemetry/clearcut-logger.js +102 -0
  8. package/build/src/telemetry/flag-utils.js +45 -0
  9. package/build/src/telemetry/metric-utils.js +14 -0
  10. package/build/src/telemetry/persistence.js +53 -0
  11. package/build/src/telemetry/types.js +33 -0
  12. package/build/src/telemetry/watchdog/clearcut-sender.js +201 -0
  13. package/build/src/telemetry/watchdog/main.js +127 -0
  14. package/build/src/telemetry/watchdog-client.js +60 -0
  15. package/build/src/third_party/devtools-formatter-worker.js +7 -0
  16. package/build/src/tools/browser.js +92 -0
  17. package/build/src/tools/extension.js +31 -0
  18. package/build/src/tools/extensions.js +79 -0
  19. package/build/src/tools/input.js +6 -1
  20. package/build/src/tools/pages.js +0 -1
  21. package/build/src/tools/tools.js +4 -0
  22. package/build/src/transports/WebMCPBridgeScript.js +11 -2
  23. package/build/src/utils/ExtensionRegistry.js +35 -0
  24. package/build/src/utils/string.js +36 -0
  25. package/build/vendor/chrome-devtools-frontend/front_end/core/common/Base64.js +20 -2
  26. package/build/vendor/chrome-devtools-frontend/front_end/core/common/Debouncer.js +8 -1
  27. package/build/vendor/chrome-devtools-frontend/front_end/core/common/Gzip.js +11 -0
  28. package/build/vendor/chrome-devtools-frontend/front_end/core/common/Object.js +6 -1
  29. package/build/vendor/chrome-devtools-frontend/front_end/core/common/ParsedURL.js +3 -0
  30. package/build/vendor/chrome-devtools-frontend/front_end/core/common/ResourceType.js +6 -0
  31. package/build/vendor/chrome-devtools-frontend/front_end/core/common/Revealer.js +0 -5
  32. package/build/vendor/chrome-devtools-frontend/front_end/core/common/Settings.js +18 -8
  33. package/build/vendor/chrome-devtools-frontend/front_end/core/host/AidaClient.js +24 -0
  34. package/build/vendor/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHostStub.js +11 -3
  35. package/build/vendor/chrome-devtools-frontend/front_end/core/host/ResourceLoader.js +1 -1
  36. package/build/vendor/chrome-devtools-frontend/front_end/core/host/UserMetrics.js +27 -20
  37. package/build/vendor/chrome-devtools-frontend/front_end/core/i18n/collect-ui-strings.js +7 -8
  38. package/build/vendor/chrome-devtools-frontend/front_end/core/i18n/generate-locales-js.js +4 -5
  39. package/build/vendor/chrome-devtools-frontend/front_end/core/platform/ArrayUtilities.js +10 -0
  40. package/build/vendor/chrome-devtools-frontend/front_end/core/platform/StringUtilities.js +63 -12
  41. package/build/vendor/chrome-devtools-frontend/front_end/core/protocol_client/CDPConnection.js +1 -0
  42. package/build/vendor/chrome-devtools-frontend/front_end/core/protocol_client/InspectorBackend.js +4 -1
  43. package/build/vendor/chrome-devtools-frontend/front_end/core/root/ExperimentNames.js +30 -0
  44. package/build/vendor/chrome-devtools-frontend/front_end/core/root/root.js +2 -1
  45. package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/AnimationModel.js +0 -4
  46. package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/CSSMatchedStyles.js +69 -9
  47. package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/CSSMetadata.js +6 -6
  48. package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/CSSModel.js +28 -13
  49. package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/CSSProperty.js +1 -1
  50. package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/CSSPropertyParserMatchers.js +6 -0
  51. package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/ConsoleModel.js +0 -2
  52. package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/CookieModel.js +1 -1
  53. package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/DOMModel.js +170 -13
  54. package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/DebuggerModel.js +5 -39
  55. package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/HeapProfilerModel.js +8 -1
  56. package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/NetworkManager.js +20 -5
  57. package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/NetworkRequest.js +12 -21
  58. package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/OverlayModel.js +19 -6
  59. package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/RehydratingConnection.js +5 -1
  60. package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/ResourceTreeModel.js +8 -5
  61. package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/SourceMap.js +15 -10
  62. package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/SourceMapManager.js +1 -1
  63. package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/SourceMapScopesInfo.js +13 -27
  64. package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/Target.js +3 -1
  65. package/build/vendor/chrome-devtools-frontend/front_end/generated/ARIAProperties.js +1 -7
  66. package/build/vendor/chrome-devtools-frontend/front_end/generated/Deprecation.js +1 -16
  67. package/build/vendor/chrome-devtools-frontend/front_end/generated/InspectorBackendCommands.js +82 -22
  68. package/build/vendor/chrome-devtools-frontend/front_end/generated/SupportedCSSProperties.js +265 -123
  69. package/build/vendor/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.js +2 -1
  70. package/build/vendor/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.js +10 -16
  71. package/build/vendor/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.js +97 -26
  72. package/build/vendor/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AICallTree.js +35 -0
  73. package/build/vendor/chrome-devtools-frontend/front_end/models/annotations/AnnotationRepository.js +163 -0
  74. package/build/vendor/chrome-devtools-frontend/front_end/models/annotations/AnnotationType.js +10 -0
  75. package/build/vendor/chrome-devtools-frontend/front_end/models/annotations/annotations.js +5 -0
  76. package/build/vendor/chrome-devtools-frontend/front_end/models/bindings/CompilerScriptMapping.js +5 -3
  77. package/build/vendor/chrome-devtools-frontend/front_end/models/bindings/DebuggerLanguagePlugins.js +29 -58
  78. package/build/vendor/chrome-devtools-frontend/front_end/models/bindings/DebuggerWorkspaceBinding.js +7 -45
  79. package/build/vendor/chrome-devtools-frontend/front_end/models/emulation/DeviceModeModel.js +1 -1
  80. package/build/vendor/chrome-devtools-frontend/front_end/models/emulation/EmulatedDevices.js +14 -0
  81. package/build/vendor/chrome-devtools-frontend/front_end/models/formatter/FormatterWorkerPool.js +8 -5
  82. package/build/vendor/chrome-devtools-frontend/front_end/models/greendev/Prototypes.js +33 -0
  83. package/build/vendor/chrome-devtools-frontend/front_end/models/greendev/greendev.js +4 -0
  84. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/ContrastCheckTrigger.js +2 -2
  85. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/CookieIssue.js +0 -21
  86. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/CorsIssue.js +1 -38
  87. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/IssueAggregator.js +8 -0
  88. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/IssuesManager.js +6 -12
  89. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/PermissionElementIssue.js +243 -0
  90. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementActivationDisabled.md +7 -0
  91. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementActivationDisabledWithOccluder.md +9 -0
  92. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementActivationDisabledWithOccluderParent.md +9 -0
  93. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementCspFrameAncestorsMissing.md +5 -0
  94. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementFencedFrameDisallowed.md +5 -0
  95. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementFontSizeTooLarge.md +5 -0
  96. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementFontSizeTooSmall.md +5 -0
  97. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementGeolocationDeprecated.md +5 -0
  98. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementInsetBoxShadowUnsupported.md +5 -0
  99. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementInvalidDisplayStyle.md +5 -0
  100. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementInvalidSizeValue.md +5 -0
  101. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementInvalidType.md +5 -0
  102. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementInvalidTypeActivation.md +5 -0
  103. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementLowContrast.md +5 -0
  104. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementNonOpaqueColor.md +5 -0
  105. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementPaddingBottomUnsupported.md +6 -0
  106. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementPaddingRightUnsupported.md +6 -0
  107. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementPermissionsPolicyBlocked.md +5 -0
  108. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementRegistrationFailed.md +5 -0
  109. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementRequestInProgress.md +5 -0
  110. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementSecurityChecksFailed.md +5 -0
  111. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementTypeNotSupported.md +5 -0
  112. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementUntrustedEvent.md +7 -0
  113. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/issues_manager.js +2 -1
  114. package/build/vendor/chrome-devtools-frontend/front_end/models/logs/NetworkLog.js +0 -8
  115. package/build/vendor/chrome-devtools-frontend/front_end/models/source_map_scopes/NamesResolver.js +4 -8
  116. package/build/vendor/chrome-devtools-frontend/front_end/models/stack_trace/StackTrace.js +30 -1
  117. package/build/vendor/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceImpl.js +70 -1
  118. package/build/vendor/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceModel.js +82 -30
  119. package/build/vendor/chrome-devtools-frontend/front_end/models/trace/EventsSerializer.js +10 -2
  120. package/build/vendor/chrome-devtools-frontend/front_end/models/trace/LanternComputationData.js +2 -2
  121. package/build/vendor/chrome-devtools-frontend/front_end/models/trace/ModelImpl.js +0 -3
  122. package/build/vendor/chrome-devtools-frontend/front_end/models/trace/Processor.js +18 -19
  123. package/build/vendor/chrome-devtools-frontend/front_end/models/trace/Styles.js +12 -4
  124. package/build/vendor/chrome-devtools-frontend/front_end/models/trace/extras/Initiators.js +46 -0
  125. package/build/vendor/chrome-devtools-frontend/front_end/models/trace/extras/TraceTree.js +4 -3
  126. package/build/vendor/chrome-devtools-frontend/front_end/models/trace/extras/extras.js +1 -0
  127. package/build/vendor/chrome-devtools-frontend/front_end/models/trace/handlers/LargestImagePaintHandler.js +2 -2
  128. package/build/vendor/chrome-devtools-frontend/front_end/models/trace/handlers/LayoutShiftsHandler.js +1 -1
  129. package/build/vendor/chrome-devtools-frontend/front_end/models/trace/handlers/MetaHandler.js +6 -0
  130. package/build/vendor/chrome-devtools-frontend/front_end/models/trace/handlers/NetworkRequestsHandler.js +10 -1
  131. package/build/vendor/chrome-devtools-frontend/front_end/models/trace/handlers/PageLoadMetricsHandler.js +44 -27
  132. package/build/vendor/chrome-devtools-frontend/front_end/models/trace/helpers/Timing.js +9 -2
  133. package/build/vendor/chrome-devtools-frontend/front_end/models/trace/insights/Common.js +1 -6
  134. package/build/vendor/chrome-devtools-frontend/front_end/models/trace/insights/LCPBreakdown.js +2 -2
  135. package/build/vendor/chrome-devtools-frontend/front_end/models/trace/insights/LCPDiscovery.js +2 -4
  136. package/build/vendor/chrome-devtools-frontend/front_end/models/trace/insights/NetworkDependencyTree.js +3 -2
  137. package/build/vendor/chrome-devtools-frontend/front_end/models/trace/insights/RenderBlocking.js +1 -1
  138. package/build/vendor/chrome-devtools-frontend/front_end/models/trace/types/TraceEvents.js +33 -11
  139. package/build/vendor/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js +51 -18
  140. package/build/vendor/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.js +1 -1
  141. package/build/vendor/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec/package/src/scopes.js +4 -0
  142. package/build/vendor/chrome-devtools-frontend/mcp/HostBindings.js +4 -0
  143. package/build/vendor/chrome-devtools-frontend/mcp/mcp.js +4 -0
  144. package/package.json +17 -10
  145. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/SameSiteInvalidSameParty.md +0 -8
  146. package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/SameSiteSamePartyCrossPartyContextSet.md +0 -10
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  [![27 Tools](https://img.shields.io/badge/MCP_Tools-27-green?style=flat-square)](./docs/tool-reference.md)
9
9
  [![Chrome](https://img.shields.io/badge/Chrome-DevTools-4285F4?style=flat-square&logo=googlechrome)](https://developer.chrome.com/docs/devtools/)
10
10
 
11
- 📖 **[WebMCP Documentation](https://docs.mcp-b.ai)** | 🚀 **[Quick Start](https://docs.mcp-b.ai/quickstart)** | 🔌 **[Connecting Agents](https://docs.mcp-b.ai/connecting-agents)** | 🎯 **[Chrome DevTools Quickstart](https://github.com/WebMCP-org/chrome-devtools-quickstart)**
11
+ **[WebMCP Documentation](https://docs.mcp-b.ai)** | **[Quick Start](https://docs.mcp-b.ai/quickstart)** | **[Connecting Agents](https://docs.mcp-b.ai/connecting-agents)** | **[Chrome DevTools Quickstart](https://github.com/WebMCP-org/chrome-devtools-quickstart)**
12
12
 
13
13
  **@mcp-b/chrome-devtools-mcp** lets AI coding agents like Claude, Gemini, Cursor, and Copilot control and inspect a live Chrome browser via the Model Context Protocol (MCP). Get performance insights, debug network requests, take screenshots, and interact with website-specific MCP tools through WebMCP integration.
14
14
 
@@ -48,13 +48,13 @@ This fork adds **WebMCP integration** - the ability to call MCP tools that are r
48
48
 
49
49
  | Feature | Chrome DevTools MCP | @mcp-b/chrome-devtools-mcp |
50
50
  |---------|--------------------|-----------------------------|
51
- | Browser automation | | |
52
- | Performance analysis | | |
53
- | Network inspection | | |
54
- | Screenshot/snapshot | | |
55
- | **Call website MCP tools** | | |
56
- | **List website MCP tools** | | |
57
- | **AI-driven tool development** | | |
51
+ | Browser automation | Yes | Yes |
52
+ | Performance analysis | Yes | Yes |
53
+ | Network inspection | Yes | Yes |
54
+ | Screenshot/snapshot | Yes | Yes |
55
+ | **Call website MCP tools** | No | Yes |
56
+ | **List website MCP tools** | No | Yes |
57
+ | **AI-driven tool development** | No | Yes |
58
58
 
59
59
  The key addition is automatic WebMCP tool discovery and registration. When you visit a page with [@mcp-b/global](https://www.npmjs.com/package/@mcp-b/global), its tools are automatically registered as first-class MCP tools that your AI agent can call directly.
60
60
 
@@ -366,6 +366,30 @@ The same way `@mcp-b/chrome-devtools-mcp` can be configured for JetBrains Junie
366
366
 
367
367
  </details>
368
368
 
369
+ <details>
370
+ <summary>Katalon Studio</summary>
371
+
372
+ The Chrome DevTools MCP server can be used with <a href="https://docs.katalon.com/katalon-studio/studioassist/mcp-servers/setting-up-chrome-devtools-mcp-server-for-studioassist">Katalon StudioAssist</a> via an MCP proxy.
373
+
374
+ **Step 1:** Install the MCP proxy by following the <a href="https://docs.katalon.com/katalon-studio/studioassist/mcp-servers/setting-up-mcp-proxy-for-stdio-mcp-servers">MCP proxy setup guide</a>.
375
+
376
+ **Step 2:** Start the Chrome DevTools MCP server with the proxy:
377
+
378
+ ```bash
379
+ mcp-proxy --transport streamablehttp --port 8080 -- npx -y @mcp-b/chrome-devtools-mcp@latest
380
+ ```
381
+
382
+ **Note:** You may need to pick another port if 8080 is already in use.
383
+
384
+ **Step 3:** In Katalon Studio, add the server to StudioAssist with the following settings:
385
+
386
+ - **Connection URL:** `http://127.0.0.1:8080/mcp`
387
+ - **Transport type:** `HTTP`
388
+
389
+ Once connected, the Chrome DevTools MCP tools will be available in StudioAssist.
390
+
391
+ </details>
392
+
369
393
  <details>
370
394
  <summary>Kiro</summary>
371
395
 
@@ -375,6 +399,25 @@ Or, from the IDE **Activity Bar** > `Kiro` > `MCP Servers` > `Click Open MCP Con
375
399
 
376
400
  </details>
377
401
 
402
+ <details>
403
+ <summary>OpenCode</summary>
404
+
405
+ Add the following configuration to your `opencode.json` file. If you don't have one, create it at `~/.config/opencode/opencode.json` (<a href="https://opencode.ai/docs/mcp-servers">guide</a>):
406
+
407
+ ```json
408
+ {
409
+ "$schema": "https://opencode.ai/config.json",
410
+ "mcp": {
411
+ "chrome-devtools": {
412
+ "type": "local",
413
+ "command": ["npx", "-y", "@mcp-b/chrome-devtools-mcp@latest"]
414
+ }
415
+ }
416
+ }
417
+ ```
418
+
419
+ </details>
420
+
378
421
  <details>
379
422
  <summary>Qoder</summary>
380
423
 
@@ -546,6 +589,11 @@ The Chrome DevTools MCP server supports the following configuration option:
546
589
 
547
590
  <!-- BEGIN AUTO GENERATED OPTIONS -->
548
591
 
592
+ - **`--autoConnect`**
593
+ If specified, automatically connects to a browser (Chrome 144+) running in the user data directory identified by the channel param. Requires the remote debugging server to be started in the Chrome instance via chrome://inspect/#remote-debugging.
594
+ - **Type:** boolean
595
+ - **Default:** `true`
596
+
549
597
  - **`--browserUrl`, `-u`**
550
598
  Connect to a running, debuggable Chrome instance (e.g. `http://127.0.0.1:9222`). For more details see: https://github.com/ChromeDevTools/chrome-devtools-mcp#connecting-to-a-running-chrome-instance.
551
599
  - **Type:** string
@@ -600,6 +648,10 @@ The Chrome DevTools MCP server supports the following configuration option:
600
648
  Additional arguments for Chrome. Only applies when Chrome is launched by `@mcp-b/chrome-devtools-mcp`.
601
649
  - **Type:** array
602
650
 
651
+ - **`--ignoreDefaultChromeArg`**
652
+ Explicitly disable default arguments for Chrome. Only applies when Chrome is launched by `@mcp-b/chrome-devtools-mcp`.
653
+ - **Type:** array
654
+
603
655
  - **`--categoryEmulation`**
604
656
  Set to false to exclude tools related to emulation.
605
657
  - **Type:** boolean
@@ -683,9 +735,65 @@ the browser is closed.
683
735
 
684
736
  ### Connecting to a running Chrome instance
685
737
 
686
- You can connect to a running Chrome instance by using the `--browser-url` option. This is useful if you want to use your existing Chrome profile or if you are running the MCP server in a sandboxed environment that does not allow starting a new Chrome instance.
738
+ By default, the Chrome DevTools MCP server will start a new Chrome instance with a dedicated profile. This might not be ideal in all situations:
687
739
 
688
- Here is a step-by-step guide on how to connect to a running Chrome Stable instance:
740
+ - If you would like to maintain the same application state when alternating between manual site testing and agent-driven testing.
741
+ - When the MCP needs to sign into a website. Some accounts may prevent sign-in when the browser is controlled via WebDriver (the default launch mechanism for the Chrome DevTools MCP server).
742
+ - If you're running your LLM inside a sandboxed environment, but you would like to connect to a Chrome instance that runs outside the sandbox.
743
+
744
+ In these cases, start Chrome first and let the Chrome DevTools MCP server connect to it. There are two ways to do so:
745
+
746
+ - **Automatic connection (available in Chrome 144+)**: best for sharing state between manual and agent-driven testing.
747
+ - **Manual connection via remote debugging port**: best when running inside a sandboxed environment.
748
+
749
+ #### Automatically connecting to a running Chrome instance
750
+
751
+ **Step 1:** Set up remote debugging in Chrome
752
+
753
+ In Chrome (>= M144), do the following to set up remote debugging:
754
+
755
+ 1. Navigate to `chrome://inspect/#remote-debugging` to enable remote debugging.
756
+ 2. Follow the dialog UI to allow or disallow incoming debugging connections.
757
+
758
+ **Step 2:** Configure the MCP server to automatically connect
759
+
760
+ To connect the `@mcp-b/chrome-devtools-mcp` server to the running Chrome instance, use
761
+ the `--autoConnect` command line argument (enabled by default):
762
+
763
+ ```json
764
+ {
765
+ "mcpServers": {
766
+ "chrome-devtools": {
767
+ "command": "npx",
768
+ "args": ["@mcp-b/chrome-devtools-mcp@latest", "--autoConnect"]
769
+ }
770
+ }
771
+ }
772
+ ```
773
+
774
+ **Step 3:** Test your setup
775
+
776
+ Make sure your browser is running. Open your MCP client and run the following prompt:
777
+
778
+ ```
779
+ Check the performance of https://developers.chrome.com
780
+ ```
781
+
782
+ > [!NOTE]
783
+ > The `autoConnect` option requires the user to start Chrome. If the user has multiple active profiles, the MCP server will connect to the default profile (as determined by Chrome). The MCP server has access to all open windows for the selected profile.
784
+
785
+ The Chrome DevTools MCP server will try to connect to your running Chrome
786
+ instance. It shows a dialog asking for user permission.
787
+
788
+ Clicking **Allow** results in the Chrome DevTools MCP server opening
789
+ [developers.chrome.com](http://developers.chrome.com) and taking a performance
790
+ trace.
791
+
792
+ #### Manual connection using port forwarding
793
+
794
+ You can connect to a running Chrome instance by using the `--browser-url` option. This is useful if you are running the MCP server in a sandboxed environment that does not allow starting a new Chrome instance.
795
+
796
+ Here is a step-by-step guide:
689
797
 
690
798
  **Step 1: Configure the MCP client**
691
799
 
@@ -748,6 +856,8 @@ For more details on remote debugging, see the [Chrome DevTools documentation](ht
748
856
 
749
857
  ## Known limitations
750
858
 
859
+ See [Troubleshooting](./docs/troubleshooting.md) for the full list.
860
+
751
861
  ### Operating system sandboxes
752
862
 
753
863
  Some MCP clients allow sandboxing the MCP server using macOS Seatbelt or Linux
@@ -160,7 +160,7 @@ export class McpContext {
160
160
  // Skip chrome:// and devtools:// pages
161
161
  const url = page.url();
162
162
  if (url.startsWith('chrome://') ||
163
- url.startsWith('chrome-extension://') ||
163
+ (!this.#options.includeExtensionPages && url.startsWith('chrome-extension://')) ||
164
164
  url.startsWith('devtools://') ||
165
165
  url === 'about:blank') {
166
166
  continue;
@@ -302,7 +302,7 @@ export class McpContext {
302
302
  // Skip chrome:// and devtools:// pages
303
303
  const url = page.url();
304
304
  if (url.startsWith('chrome://') ||
305
- url.startsWith('chrome-extension://') ||
305
+ (!this.#options.includeExtensionPages && url.startsWith('chrome-extension://')) ||
306
306
  url.startsWith('devtools://')) {
307
307
  return;
308
308
  }
@@ -317,7 +317,7 @@ export class McpContext {
317
317
  // Skip internal pages
318
318
  const newUrl = page.url();
319
319
  if (newUrl.startsWith('chrome://') ||
320
- newUrl.startsWith('chrome-extension://') ||
320
+ (!this.#options.includeExtensionPages && newUrl.startsWith('chrome-extension://')) ||
321
321
  newUrl.startsWith('devtools://') ||
322
322
  newUrl === 'about:blank') {
323
323
  return;
@@ -1053,6 +1053,53 @@ export class McpContext {
1053
1053
  };
1054
1054
  }
1055
1055
  }
1056
+ /**
1057
+ * Evaluate a JavaScript expression in a Chrome extension's background context.
1058
+ *
1059
+ * Supports both MV3 service workers (type "service_worker") and MV2/dev-mode
1060
+ * background pages (type "background_page"). Uses waitForTarget to handle
1061
+ * cases where the worker hasn't been discovered yet.
1062
+ *
1063
+ * @param expression - JavaScript expression to evaluate.
1064
+ * @param extensionId - Optional extension ID. If omitted, uses the first found.
1065
+ * @returns The result of the evaluation.
1066
+ */
1067
+ async evaluateInExtensionWorker(expression, extensionId) {
1068
+ const isExtensionBackground = (t) => (t.type() === 'service_worker' || t.type() === 'background_page') &&
1069
+ t.url().startsWith('chrome-extension://') &&
1070
+ (!extensionId || t.url().includes(extensionId));
1071
+ // First check already-known targets
1072
+ let target = this.browser.targets().find(isExtensionBackground);
1073
+ // If not found, wait briefly for the target to appear (handles startup timing)
1074
+ if (!target) {
1075
+ try {
1076
+ target = await this.browser.waitForTarget(isExtensionBackground, { timeout: 5000 });
1077
+ }
1078
+ catch {
1079
+ throw new Error('No extension background target found. Ensure the extension is loaded in the browser. ' +
1080
+ 'If connecting to an existing browser, verify the extension is installed and active.');
1081
+ }
1082
+ }
1083
+ if (target.type() === 'service_worker') {
1084
+ const worker = await target.worker();
1085
+ if (!worker) {
1086
+ throw new Error('Could not attach to service worker');
1087
+ }
1088
+ return worker.evaluate(expression);
1089
+ }
1090
+ // background_page — get a Page handle instead
1091
+ const page = await target.page();
1092
+ if (!page) {
1093
+ throw new Error('Could not attach to extension background page');
1094
+ }
1095
+ return page.evaluate(expression);
1096
+ }
1097
+ async reconnectBrowser(options) {
1098
+ if (!this.#options.onReconnect) {
1099
+ throw new Error('Browser reconnection is not supported in this configuration');
1100
+ }
1101
+ return this.#options.onReconnect(options);
1102
+ }
1056
1103
  /**
1057
1104
  * We need to ignore favicon request as they make our test flaky
1058
1105
  */
@@ -18,10 +18,10 @@ let browser;
18
18
  *
19
19
  * @returns A filter function for Puppeteer's targetFilter option.
20
20
  */
21
- function makeTargetFilter() {
21
+ function makeTargetFilter(includeExtensionPages = false) {
22
22
  const ignoredPrefixes = new Set([
23
23
  'chrome://',
24
- 'chrome-extension://',
24
+ ...(includeExtensionPages ? [] : ['chrome-extension://']),
25
25
  'chrome-untrusted://',
26
26
  ]);
27
27
  return function targetFilter(target) {
@@ -53,14 +53,18 @@ function makeTargetFilter() {
53
53
  * @throws Error if connection fails or no connection method specified.
54
54
  */
55
55
  export async function ensureBrowserConnected(options) {
56
- const { channel } = options;
56
+ const { channel, includeExtensionPages } = options;
57
57
  if (browser?.connected) {
58
58
  return browser;
59
59
  }
60
60
  const connectOptions = {
61
- targetFilter: makeTargetFilter(),
61
+ targetFilter: makeTargetFilter(includeExtensionPages),
62
62
  defaultViewport: null,
63
63
  handleDevToolsAsPage: true,
64
+ ...(includeExtensionPages && {
65
+ isPageTargetCallback: (target) => target.type() === 'page' ||
66
+ target.url().startsWith('chrome-extension://'),
67
+ }),
64
68
  };
65
69
  if (options.wsEndpoint) {
66
70
  connectOptions.browserWSEndpoint = options.wsEndpoint;
@@ -201,7 +205,7 @@ export async function launch(options) {
201
205
  try {
202
206
  const browser = await puppeteer.launch({
203
207
  channel: puppeteerChannel,
204
- targetFilter: makeTargetFilter(),
208
+ targetFilter: makeTargetFilter(options.includeExtensionPages),
205
209
  executablePath,
206
210
  defaultViewport: null,
207
211
  userDataDir,
@@ -210,6 +214,11 @@ export async function launch(options) {
210
214
  args,
211
215
  acceptInsecureCerts: options.acceptInsecureCerts,
212
216
  handleDevToolsAsPage: true,
217
+ ...(options.includeExtensionPages && {
218
+ enableExtensions: true,
219
+ isPageTargetCallback: (target) => target.type === 'page' ||
220
+ target.url?.startsWith('chrome-extension://') === true,
221
+ }),
213
222
  });
214
223
  if (options.logFile) {
215
224
  // FIXME: we are probably subscribing too late to catch startup logs. We
@@ -219,7 +228,6 @@ export async function launch(options) {
219
228
  }
220
229
  if (options.viewport) {
221
230
  const [page] = await browser.pages();
222
- // @ts-expect-error internal API for now.
223
231
  await page?.resize({
224
232
  contentWidth: options.viewport.width,
225
233
  contentHeight: options.viewport.height,
@@ -250,3 +258,51 @@ export async function ensureBrowserLaunched(options) {
250
258
  browser = await launch(options);
251
259
  return browser;
252
260
  }
261
+ /**
262
+ * Disconnect from the current browser without killing the process.
263
+ * Clears the cached browser reference so future calls can connect to a new instance.
264
+ */
265
+ export function disconnectBrowser() {
266
+ if (browser) {
267
+ try {
268
+ browser.disconnect();
269
+ }
270
+ catch {
271
+ // Ignore disconnect errors — browser may already be gone
272
+ }
273
+ browser = undefined;
274
+ }
275
+ }
276
+ /**
277
+ * Connect to a new browser instance, replacing the cached reference.
278
+ *
279
+ * @param options - Connection options (browserURL or wsEndpoint).
280
+ * @returns Connected browser instance.
281
+ */
282
+ export async function connectToNewBrowser(options) {
283
+ const connectOptions = {
284
+ targetFilter: makeTargetFilter(options.includeExtensionPages),
285
+ defaultViewport: null,
286
+ handleDevToolsAsPage: true,
287
+ ...(options.includeExtensionPages && {
288
+ isPageTargetCallback: (target) => target.type() === 'page' ||
289
+ target.url().startsWith('chrome-extension://'),
290
+ }),
291
+ };
292
+ if (options.wsEndpoint) {
293
+ connectOptions.browserWSEndpoint = options.wsEndpoint;
294
+ if (options.wsHeaders) {
295
+ connectOptions.headers = options.wsHeaders;
296
+ }
297
+ }
298
+ else if (options.browserURL) {
299
+ connectOptions.browserURL = options.browserURL;
300
+ }
301
+ else {
302
+ throw new Error('Either browserURL or wsEndpoint must be provided');
303
+ }
304
+ logger('Connecting Puppeteer to new browser:', JSON.stringify(connectOptions));
305
+ browser = await puppeteer.connect(connectOptions);
306
+ logger('Connected to new browser');
307
+ return browser;
308
+ }
package/build/src/cli.js CHANGED
@@ -162,6 +162,11 @@ export const cliOptions = {
162
162
  default: true,
163
163
  describe: 'Set to false to exclude tools related to network.',
164
164
  },
165
+ includeExtensionPages: {
166
+ type: 'boolean',
167
+ describe: 'Include chrome-extension:// pages in page listing and tool discovery.',
168
+ default: false,
169
+ },
165
170
  };
166
171
  export function parseArguments(version, argv = process.argv) {
167
172
  const yargsInstance = yargs(hideBin(argv))
@@ -175,7 +180,7 @@ export function parseArguments(version, argv = process.argv) {
175
180
  args.browserUrl === undefined &&
176
181
  args.wsEndpoint === undefined &&
177
182
  args.executablePath === undefined) {
178
- args.channel = 'dev';
183
+ args.channel = 'stable';
179
184
  }
180
185
  return true;
181
186
  })
@@ -0,0 +1,190 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { ISSUE_UTILS } from '../issue-descriptions.js';
7
+ import { logger } from '../logger.js';
8
+ import { DevTools } from '../third_party/index.js';
9
+ export class IssueFormatter {
10
+ #issue;
11
+ #options;
12
+ constructor(issue, options) {
13
+ this.#issue = issue;
14
+ this.#options = options;
15
+ }
16
+ toString() {
17
+ const title = this.#getTitle();
18
+ const count = this.#issue.getAggregatedIssuesCount();
19
+ const idPart = this.#options.id !== undefined ? `msgid=${this.#options.id} ` : '';
20
+ return `${idPart}[issue] ${title} (count: ${count})`;
21
+ }
22
+ toStringDetailed() {
23
+ const result = [];
24
+ if (this.#options.id !== undefined) {
25
+ result.push(`ID: ${this.#options.id}`);
26
+ }
27
+ const bodyParts = [];
28
+ const description = this.#getDescription();
29
+ let processedMarkdown = description?.trim();
30
+ // Remove heading in order not to conflict with the whole console message response markdown
31
+ if (processedMarkdown?.startsWith('# ')) {
32
+ processedMarkdown = processedMarkdown.substring(2).trimStart();
33
+ }
34
+ if (processedMarkdown) {
35
+ bodyParts.push(processedMarkdown);
36
+ }
37
+ else {
38
+ bodyParts.push(this.#getTitle() ?? 'Unknown Issue');
39
+ }
40
+ const links = this.#issue.getDescription()?.links;
41
+ if (links && links.length > 0) {
42
+ bodyParts.push('Learn more:');
43
+ for (const link of links) {
44
+ bodyParts.push(`[${link.linkTitle}](${link.link})`);
45
+ }
46
+ }
47
+ const affectedResources = this.#getAffectedResources();
48
+ if (affectedResources.length) {
49
+ bodyParts.push('### Affected resources');
50
+ bodyParts.push(...affectedResources.map(item => {
51
+ const details = [];
52
+ if (item.uid) {
53
+ details.push(`uid=${item.uid}`);
54
+ }
55
+ if (item.request) {
56
+ details.push((typeof item.request === 'number' ? `reqid=` : 'url=') +
57
+ item.request);
58
+ }
59
+ if (item.data) {
60
+ details.push(`data=${JSON.stringify(item.data)}`);
61
+ }
62
+ return details.join(' ');
63
+ }));
64
+ }
65
+ result.push(`Message: issue> ${bodyParts.join('\n')}`);
66
+ return result.join('\n');
67
+ }
68
+ toJSON() {
69
+ return {
70
+ type: 'issue',
71
+ title: this.#getTitle(),
72
+ count: this.#issue.getAggregatedIssuesCount(),
73
+ id: this.#options.id,
74
+ };
75
+ }
76
+ toJSONDetailed() {
77
+ return {
78
+ id: this.#options.id,
79
+ type: 'issue',
80
+ title: this.#getTitle(),
81
+ description: this.#getDescription(),
82
+ links: this.#issue.getDescription()?.links,
83
+ affectedResources: this.#getAffectedResources(),
84
+ };
85
+ }
86
+ #getAffectedResources() {
87
+ const issues = this.#issue.getAllIssues();
88
+ const affectedResources = [];
89
+ for (const singleIssue of issues) {
90
+ const details = singleIssue.details();
91
+ if (!details) {
92
+ continue;
93
+ }
94
+ // We send the remaining details as untyped JSON because the DevTools
95
+ // frontend code is currently not re-usable.
96
+ const data = structuredClone(details);
97
+ let uid;
98
+ let request;
99
+ if ('violatingNodeId' in details &&
100
+ details.violatingNodeId &&
101
+ this.#options.elementIdResolver) {
102
+ uid = this.#options.elementIdResolver(details.violatingNodeId);
103
+ delete data.violatingNodeId;
104
+ }
105
+ if ('nodeId' in details &&
106
+ details.nodeId &&
107
+ this.#options.elementIdResolver) {
108
+ uid = this.#options.elementIdResolver(details.nodeId);
109
+ delete data.nodeId;
110
+ }
111
+ if ('documentNodeId' in details &&
112
+ details.documentNodeId &&
113
+ this.#options.elementIdResolver) {
114
+ uid = this.#options.elementIdResolver(details.documentNodeId);
115
+ delete data.documentNodeId;
116
+ }
117
+ if ('request' in details && details.request) {
118
+ request = details.request.url;
119
+ if (details.request.requestId && this.#options.requestIdResolver) {
120
+ const resolvedId = this.#options.requestIdResolver(details.request.requestId);
121
+ if (resolvedId) {
122
+ request = resolvedId;
123
+ const requestData = data.request;
124
+ delete requestData.requestId;
125
+ }
126
+ }
127
+ }
128
+ // These fields has no use for the MCP client (redundant or irrelevant).
129
+ delete data.errorType;
130
+ delete data.frameId;
131
+ affectedResources.push({
132
+ uid,
133
+ data: data,
134
+ request,
135
+ });
136
+ }
137
+ return affectedResources;
138
+ }
139
+ isValid() {
140
+ return this.#getTitle() !== undefined;
141
+ }
142
+ // Helper to extract title
143
+ #getTitle() {
144
+ const markdownDescription = this.#issue.getDescription();
145
+ const filename = markdownDescription?.file;
146
+ if (!filename) {
147
+ logger(`no description found for issue:` + this.#issue.code());
148
+ return undefined;
149
+ }
150
+ // We already have the description logic in #getDescription, but title extraction is separate
151
+ // We can reuse the logic or cache it.
152
+ // Ideally we should process markdown once.
153
+ const rawMarkdown = ISSUE_UTILS.getIssueDescription(filename);
154
+ if (!rawMarkdown) {
155
+ logger(`no markdown ${filename} found for issue:` + this.#issue.code());
156
+ return undefined;
157
+ }
158
+ try {
159
+ const processedMarkdown = DevTools.MarkdownIssueDescription.substitutePlaceholders(rawMarkdown, markdownDescription?.substitutions);
160
+ const markdownAst = DevTools.Marked.Marked.lexer(processedMarkdown);
161
+ const title = DevTools.MarkdownIssueDescription.findTitleFromMarkdownAst(markdownAst);
162
+ if (!title) {
163
+ logger('cannot read issue title from ' + filename);
164
+ return undefined;
165
+ }
166
+ return title;
167
+ }
168
+ catch {
169
+ logger('error parsing markdown for issue ' + this.#issue.code());
170
+ return undefined;
171
+ }
172
+ }
173
+ #getDescription() {
174
+ const markdownDescription = this.#issue.getDescription();
175
+ const filename = markdownDescription?.file;
176
+ if (!filename) {
177
+ return undefined;
178
+ }
179
+ const rawMarkdown = ISSUE_UTILS.getIssueDescription(filename);
180
+ if (!rawMarkdown) {
181
+ return undefined;
182
+ }
183
+ try {
184
+ return DevTools.MarkdownIssueDescription.substitutePlaceholders(rawMarkdown, markdownDescription?.substitutions);
185
+ }
186
+ catch {
187
+ return undefined;
188
+ }
189
+ }
190
+ }