@mcp-b/chrome-devtools-mcp 2.0.2 → 2.0.5
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.
- package/README.md +120 -10
- package/build/src/McpContext.js +69 -5
- package/build/src/browser.js +157 -51
- package/build/src/cli.js +11 -5
- package/build/src/formatters/IssueFormatter.js +190 -0
- package/build/src/main.js +83 -2
- package/build/src/telemetry/clearcut-logger.js +102 -0
- package/build/src/telemetry/flag-utils.js +45 -0
- package/build/src/telemetry/metric-utils.js +14 -0
- package/build/src/telemetry/persistence.js +53 -0
- package/build/src/telemetry/types.js +33 -0
- package/build/src/telemetry/watchdog/clearcut-sender.js +201 -0
- package/build/src/telemetry/watchdog/main.js +127 -0
- package/build/src/telemetry/watchdog-client.js +60 -0
- package/build/src/third_party/devtools-formatter-worker.js +7 -0
- package/build/src/third_party/index.js +1 -1
- package/build/src/tools/browser.js +92 -0
- package/build/src/tools/extension.js +31 -0
- package/build/src/tools/extensions.js +79 -0
- package/build/src/tools/input.js +6 -1
- package/build/src/tools/pages.js +7 -1
- package/build/src/tools/script.js +31 -4
- package/build/src/tools/tools.js +4 -0
- package/build/src/transports/CDPClientTransport.js +184 -0
- package/build/src/transports/WebMCPBridgeScript.js +11 -2
- package/build/src/utils/ExtensionRegistry.js +35 -0
- package/build/src/utils/string.js +36 -0
- package/build/vendor/chrome-devtools-frontend/front_end/core/common/Base64.js +20 -2
- package/build/vendor/chrome-devtools-frontend/front_end/core/common/Debouncer.js +8 -1
- package/build/vendor/chrome-devtools-frontend/front_end/core/common/Gzip.js +11 -0
- package/build/vendor/chrome-devtools-frontend/front_end/core/common/Object.js +6 -1
- package/build/vendor/chrome-devtools-frontend/front_end/core/common/ParsedURL.js +3 -0
- package/build/vendor/chrome-devtools-frontend/front_end/core/common/ResourceType.js +6 -0
- package/build/vendor/chrome-devtools-frontend/front_end/core/common/Revealer.js +0 -5
- package/build/vendor/chrome-devtools-frontend/front_end/core/common/Settings.js +18 -8
- package/build/vendor/chrome-devtools-frontend/front_end/core/host/AidaClient.js +24 -0
- package/build/vendor/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHostStub.js +11 -3
- package/build/vendor/chrome-devtools-frontend/front_end/core/host/ResourceLoader.js +1 -1
- package/build/vendor/chrome-devtools-frontend/front_end/core/host/UserMetrics.js +27 -20
- package/build/vendor/chrome-devtools-frontend/front_end/core/i18n/collect-ui-strings.js +7 -8
- package/build/vendor/chrome-devtools-frontend/front_end/core/i18n/generate-locales-js.js +4 -5
- package/build/vendor/chrome-devtools-frontend/front_end/core/platform/ArrayUtilities.js +10 -0
- package/build/vendor/chrome-devtools-frontend/front_end/core/platform/StringUtilities.js +63 -12
- package/build/vendor/chrome-devtools-frontend/front_end/core/protocol_client/CDPConnection.js +1 -0
- package/build/vendor/chrome-devtools-frontend/front_end/core/protocol_client/InspectorBackend.js +4 -1
- package/build/vendor/chrome-devtools-frontend/front_end/core/root/ExperimentNames.js +30 -0
- package/build/vendor/chrome-devtools-frontend/front_end/core/root/root.js +2 -1
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/AnimationModel.js +0 -4
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/CSSMatchedStyles.js +69 -9
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/CSSMetadata.js +6 -6
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/CSSModel.js +28 -13
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/CSSProperty.js +1 -1
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/CSSPropertyParserMatchers.js +6 -0
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/ConsoleModel.js +0 -2
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/CookieModel.js +1 -1
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/DOMModel.js +170 -13
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/DebuggerModel.js +5 -39
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/HeapProfilerModel.js +8 -1
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/NetworkManager.js +20 -5
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/NetworkRequest.js +12 -21
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/OverlayModel.js +19 -6
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/RehydratingConnection.js +5 -1
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/ResourceTreeModel.js +8 -5
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/SourceMap.js +15 -10
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/SourceMapManager.js +1 -1
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/SourceMapScopesInfo.js +13 -27
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/Target.js +3 -1
- package/build/vendor/chrome-devtools-frontend/front_end/generated/ARIAProperties.js +1 -7
- package/build/vendor/chrome-devtools-frontend/front_end/generated/Deprecation.js +1 -16
- package/build/vendor/chrome-devtools-frontend/front_end/generated/InspectorBackendCommands.js +82 -22
- package/build/vendor/chrome-devtools-frontend/front_end/generated/SupportedCSSProperties.js +265 -123
- package/build/vendor/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.js +2 -1
- package/build/vendor/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.js +10 -16
- package/build/vendor/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.js +97 -26
- package/build/vendor/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AICallTree.js +35 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/annotations/AnnotationRepository.js +163 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/annotations/AnnotationType.js +10 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/annotations/annotations.js +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/bindings/CompilerScriptMapping.js +5 -3
- package/build/vendor/chrome-devtools-frontend/front_end/models/bindings/DebuggerLanguagePlugins.js +29 -58
- package/build/vendor/chrome-devtools-frontend/front_end/models/bindings/DebuggerWorkspaceBinding.js +7 -45
- package/build/vendor/chrome-devtools-frontend/front_end/models/emulation/DeviceModeModel.js +1 -1
- package/build/vendor/chrome-devtools-frontend/front_end/models/emulation/EmulatedDevices.js +14 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/formatter/FormatterWorkerPool.js +8 -5
- package/build/vendor/chrome-devtools-frontend/front_end/models/greendev/Prototypes.js +33 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/greendev/greendev.js +4 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/ContrastCheckTrigger.js +2 -2
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/CookieIssue.js +0 -21
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/CorsIssue.js +1 -38
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/IssueAggregator.js +8 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/IssuesManager.js +6 -12
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/PermissionElementIssue.js +243 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementActivationDisabled.md +7 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementActivationDisabledWithOccluder.md +9 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementActivationDisabledWithOccluderParent.md +9 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementCspFrameAncestorsMissing.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementFencedFrameDisallowed.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementFontSizeTooLarge.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementFontSizeTooSmall.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementGeolocationDeprecated.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementInsetBoxShadowUnsupported.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementInvalidDisplayStyle.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementInvalidSizeValue.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementInvalidType.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementInvalidTypeActivation.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementLowContrast.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementNonOpaqueColor.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementPaddingBottomUnsupported.md +6 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementPaddingRightUnsupported.md +6 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementPermissionsPolicyBlocked.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementRegistrationFailed.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementRequestInProgress.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementSecurityChecksFailed.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementTypeNotSupported.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementUntrustedEvent.md +7 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/issues_manager.js +2 -1
- package/build/vendor/chrome-devtools-frontend/front_end/models/logs/NetworkLog.js +0 -8
- package/build/vendor/chrome-devtools-frontend/front_end/models/source_map_scopes/NamesResolver.js +4 -8
- package/build/vendor/chrome-devtools-frontend/front_end/models/stack_trace/StackTrace.js +30 -1
- package/build/vendor/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceImpl.js +70 -1
- package/build/vendor/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceModel.js +82 -30
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/EventsSerializer.js +10 -2
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/LanternComputationData.js +2 -2
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/ModelImpl.js +0 -3
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/Processor.js +18 -19
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/Styles.js +12 -4
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/extras/Initiators.js +46 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/extras/TraceTree.js +4 -3
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/extras/extras.js +1 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/handlers/LargestImagePaintHandler.js +2 -2
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/handlers/LayoutShiftsHandler.js +1 -1
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/handlers/MetaHandler.js +6 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/handlers/NetworkRequestsHandler.js +10 -1
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/handlers/PageLoadMetricsHandler.js +44 -27
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/helpers/Timing.js +9 -2
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/insights/Common.js +1 -6
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/insights/LCPBreakdown.js +2 -2
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/insights/LCPDiscovery.js +2 -4
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/insights/NetworkDependencyTree.js +3 -2
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/insights/RenderBlocking.js +1 -1
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/types/TraceEvents.js +33 -11
- package/build/vendor/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js +51 -18
- package/build/vendor/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.js +1 -1
- package/build/vendor/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec/package/src/scopes.js +4 -0
- package/build/vendor/chrome-devtools-frontend/mcp/HostBindings.js +4 -0
- package/build/vendor/chrome-devtools-frontend/mcp/mcp.js +4 -0
- package/package.json +28 -21
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/SameSiteInvalidSameParty.md +0 -8
- 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
|
[](./docs/tool-reference.md)
|
|
9
9
|
[](https://developer.chrome.com/docs/devtools/)
|
|
10
10
|
|
|
11
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
package/build/src/McpContext.js
CHANGED
|
@@ -89,6 +89,7 @@ export class McpContext {
|
|
|
89
89
|
#networkConditionsMap = new WeakMap();
|
|
90
90
|
#cpuThrottlingRateMap = new WeakMap();
|
|
91
91
|
#geolocationMap = new WeakMap();
|
|
92
|
+
#bypassCSPMap = new WeakMap();
|
|
92
93
|
#dialog;
|
|
93
94
|
#nextSnapshotId = 1;
|
|
94
95
|
#traceResults = [];
|
|
@@ -160,7 +161,7 @@ export class McpContext {
|
|
|
160
161
|
// Skip chrome:// and devtools:// pages
|
|
161
162
|
const url = page.url();
|
|
162
163
|
if (url.startsWith('chrome://') ||
|
|
163
|
-
url.startsWith('chrome-extension://') ||
|
|
164
|
+
(!this.#options.includeExtensionPages && url.startsWith('chrome-extension://')) ||
|
|
164
165
|
url.startsWith('devtools://') ||
|
|
165
166
|
url === 'about:blank') {
|
|
166
167
|
continue;
|
|
@@ -302,7 +303,7 @@ export class McpContext {
|
|
|
302
303
|
// Skip chrome:// and devtools:// pages
|
|
303
304
|
const url = page.url();
|
|
304
305
|
if (url.startsWith('chrome://') ||
|
|
305
|
-
url.startsWith('chrome-extension://') ||
|
|
306
|
+
(!this.#options.includeExtensionPages && url.startsWith('chrome-extension://')) ||
|
|
306
307
|
url.startsWith('devtools://')) {
|
|
307
308
|
return;
|
|
308
309
|
}
|
|
@@ -317,7 +318,7 @@ export class McpContext {
|
|
|
317
318
|
// Skip internal pages
|
|
318
319
|
const newUrl = page.url();
|
|
319
320
|
if (newUrl.startsWith('chrome://') ||
|
|
320
|
-
newUrl.startsWith('chrome-extension://') ||
|
|
321
|
+
(!this.#options.includeExtensionPages && newUrl.startsWith('chrome-extension://')) ||
|
|
321
322
|
newUrl.startsWith('devtools://') ||
|
|
322
323
|
newUrl === 'about:blank') {
|
|
323
324
|
return;
|
|
@@ -616,6 +617,20 @@ export class McpContext {
|
|
|
616
617
|
const page = this.getSelectedPage();
|
|
617
618
|
return this.#geolocationMap.get(page) ?? null;
|
|
618
619
|
}
|
|
620
|
+
async setBypassCSP(enabled) {
|
|
621
|
+
const page = this.getSelectedPage();
|
|
622
|
+
await page.setBypassCSP(enabled);
|
|
623
|
+
if (enabled) {
|
|
624
|
+
this.#bypassCSPMap.set(page, true);
|
|
625
|
+
}
|
|
626
|
+
else {
|
|
627
|
+
this.#bypassCSPMap.delete(page);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
getBypassCSP() {
|
|
631
|
+
const page = this.getSelectedPage();
|
|
632
|
+
return this.#bypassCSPMap.get(page) ?? false;
|
|
633
|
+
}
|
|
619
634
|
setIsRunningPerformanceTrace(x) {
|
|
620
635
|
this.#isRunningTrace = x;
|
|
621
636
|
}
|
|
@@ -916,7 +931,8 @@ export class McpContext {
|
|
|
916
931
|
try {
|
|
917
932
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'chrome-devtools-mcp-'));
|
|
918
933
|
const filename = path.join(dir, `screenshot.${getExtensionFromMimeType(mimeType)}`);
|
|
919
|
-
|
|
934
|
+
// Use mode 0o600 (owner read/write only) for secure temp file creation
|
|
935
|
+
await fs.writeFile(filename, data, { mode: 0o600 });
|
|
920
936
|
return { filename };
|
|
921
937
|
}
|
|
922
938
|
catch (err) {
|
|
@@ -927,7 +943,8 @@ export class McpContext {
|
|
|
927
943
|
async saveFile(data, filename) {
|
|
928
944
|
try {
|
|
929
945
|
const filePath = path.resolve(filename);
|
|
930
|
-
|
|
946
|
+
// Use mode 0o644 (owner read/write, others read) for user-specified paths
|
|
947
|
+
await fs.writeFile(filePath, data, { mode: 0o644 });
|
|
931
948
|
return { filename };
|
|
932
949
|
}
|
|
933
950
|
catch (err) {
|
|
@@ -1051,6 +1068,53 @@ export class McpContext {
|
|
|
1051
1068
|
};
|
|
1052
1069
|
}
|
|
1053
1070
|
}
|
|
1071
|
+
/**
|
|
1072
|
+
* Evaluate a JavaScript expression in a Chrome extension's background context.
|
|
1073
|
+
*
|
|
1074
|
+
* Supports both MV3 service workers (type "service_worker") and MV2/dev-mode
|
|
1075
|
+
* background pages (type "background_page"). Uses waitForTarget to handle
|
|
1076
|
+
* cases where the worker hasn't been discovered yet.
|
|
1077
|
+
*
|
|
1078
|
+
* @param expression - JavaScript expression to evaluate.
|
|
1079
|
+
* @param extensionId - Optional extension ID. If omitted, uses the first found.
|
|
1080
|
+
* @returns The result of the evaluation.
|
|
1081
|
+
*/
|
|
1082
|
+
async evaluateInExtensionWorker(expression, extensionId) {
|
|
1083
|
+
const isExtensionBackground = (t) => (t.type() === 'service_worker' || t.type() === 'background_page') &&
|
|
1084
|
+
t.url().startsWith('chrome-extension://') &&
|
|
1085
|
+
(!extensionId || t.url().includes(extensionId));
|
|
1086
|
+
// First check already-known targets
|
|
1087
|
+
let target = this.browser.targets().find(isExtensionBackground);
|
|
1088
|
+
// If not found, wait briefly for the target to appear (handles startup timing)
|
|
1089
|
+
if (!target) {
|
|
1090
|
+
try {
|
|
1091
|
+
target = await this.browser.waitForTarget(isExtensionBackground, { timeout: 5000 });
|
|
1092
|
+
}
|
|
1093
|
+
catch {
|
|
1094
|
+
throw new Error('No extension background target found. Ensure the extension is loaded in the browser. ' +
|
|
1095
|
+
'If connecting to an existing browser, verify the extension is installed and active.');
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
if (target.type() === 'service_worker') {
|
|
1099
|
+
const worker = await target.worker();
|
|
1100
|
+
if (!worker) {
|
|
1101
|
+
throw new Error('Could not attach to service worker');
|
|
1102
|
+
}
|
|
1103
|
+
return worker.evaluate(expression);
|
|
1104
|
+
}
|
|
1105
|
+
// background_page — get a Page handle instead
|
|
1106
|
+
const page = await target.page();
|
|
1107
|
+
if (!page) {
|
|
1108
|
+
throw new Error('Could not attach to extension background page');
|
|
1109
|
+
}
|
|
1110
|
+
return page.evaluate(expression);
|
|
1111
|
+
}
|
|
1112
|
+
async reconnectBrowser(options) {
|
|
1113
|
+
if (!this.#options.onReconnect) {
|
|
1114
|
+
throw new Error('Browser reconnection is not supported in this configuration');
|
|
1115
|
+
}
|
|
1116
|
+
return this.#options.onReconnect(options);
|
|
1117
|
+
}
|
|
1054
1118
|
/**
|
|
1055
1119
|
* We need to ignore favicon request as they make our test flaky
|
|
1056
1120
|
*/
|
package/build/src/browser.js
CHANGED
|
@@ -10,6 +10,61 @@ import { logger } from './logger.js';
|
|
|
10
10
|
import { puppeteer } from './third_party/index.js';
|
|
11
11
|
/** Cached browser instance for reuse across calls. */
|
|
12
12
|
let browser;
|
|
13
|
+
/**
|
|
14
|
+
* Get Chrome's default user data directory for the given platform and channel.
|
|
15
|
+
*
|
|
16
|
+
* @returns The platform-specific path to Chrome's user data directory.
|
|
17
|
+
*/
|
|
18
|
+
function getChromeDefaultUserDataDir(channel = 'stable') {
|
|
19
|
+
const platform = os.platform();
|
|
20
|
+
if (platform === 'darwin') {
|
|
21
|
+
const suffix = channel === 'stable'
|
|
22
|
+
? ''
|
|
23
|
+
: ` ${channel.charAt(0).toUpperCase() + channel.slice(1)}`;
|
|
24
|
+
return path.join(os.homedir(), 'Library', 'Application Support', 'Google', `Chrome${suffix}`);
|
|
25
|
+
}
|
|
26
|
+
if (platform === 'win32') {
|
|
27
|
+
const appData = process.env.LOCALAPPDATA ?? path.join(os.homedir(), 'AppData', 'Local');
|
|
28
|
+
const suffix = channel === 'stable'
|
|
29
|
+
? ''
|
|
30
|
+
: ` ${channel.charAt(0).toUpperCase() + channel.slice(1)}`;
|
|
31
|
+
return path.join(appData, 'Google', `Chrome${suffix}`, 'User Data');
|
|
32
|
+
}
|
|
33
|
+
// Linux
|
|
34
|
+
const channelSuffix = channel === 'stable'
|
|
35
|
+
? ''
|
|
36
|
+
: channel === 'beta'
|
|
37
|
+
? '-beta'
|
|
38
|
+
: '-unstable';
|
|
39
|
+
return path.join(os.homedir(), '.config', `google-chrome${channelSuffix}`);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Try to read a WebSocket endpoint from a DevToolsActivePort file.
|
|
43
|
+
*
|
|
44
|
+
* @param userDataDir - Directory containing the DevToolsActivePort file.
|
|
45
|
+
* @returns The WebSocket endpoint URL, or undefined if the file doesn't exist or is invalid.
|
|
46
|
+
*/
|
|
47
|
+
function readDevToolsActivePort(userDataDir) {
|
|
48
|
+
const portPath = path.join(userDataDir, 'DevToolsActivePort');
|
|
49
|
+
try {
|
|
50
|
+
const fileContent = fs.readFileSync(portPath, 'utf8');
|
|
51
|
+
const [rawPort, rawPath] = fileContent
|
|
52
|
+
.split('\n')
|
|
53
|
+
.map(line => line.trim())
|
|
54
|
+
.filter(line => !!line);
|
|
55
|
+
if (!rawPort || !rawPath) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
const port = parseInt(rawPort, 10);
|
|
59
|
+
if (isNaN(port) || port <= 0 || port > 65535) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
return `ws://127.0.0.1:${port}${rawPath}`;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
13
68
|
/**
|
|
14
69
|
* Create a target filter for Puppeteer that excludes internal Chrome pages.
|
|
15
70
|
*
|
|
@@ -18,10 +73,10 @@ let browser;
|
|
|
18
73
|
*
|
|
19
74
|
* @returns A filter function for Puppeteer's targetFilter option.
|
|
20
75
|
*/
|
|
21
|
-
function makeTargetFilter() {
|
|
76
|
+
function makeTargetFilter(includeExtensionPages = false) {
|
|
22
77
|
const ignoredPrefixes = new Set([
|
|
23
78
|
'chrome://',
|
|
24
|
-
'chrome-extension://',
|
|
79
|
+
...(includeExtensionPages ? [] : ['chrome-extension://']),
|
|
25
80
|
'chrome-untrusted://',
|
|
26
81
|
]);
|
|
27
82
|
return function targetFilter(target) {
|
|
@@ -53,14 +108,18 @@ function makeTargetFilter() {
|
|
|
53
108
|
* @throws Error if connection fails or no connection method specified.
|
|
54
109
|
*/
|
|
55
110
|
export async function ensureBrowserConnected(options) {
|
|
56
|
-
const { channel } = options;
|
|
111
|
+
const { channel, includeExtensionPages } = options;
|
|
57
112
|
if (browser?.connected) {
|
|
58
113
|
return browser;
|
|
59
114
|
}
|
|
60
115
|
const connectOptions = {
|
|
61
|
-
targetFilter: makeTargetFilter(),
|
|
116
|
+
targetFilter: makeTargetFilter(includeExtensionPages),
|
|
62
117
|
defaultViewport: null,
|
|
63
118
|
handleDevToolsAsPage: true,
|
|
119
|
+
...(includeExtensionPages && {
|
|
120
|
+
isPageTargetCallback: (target) => target.type() === 'page' ||
|
|
121
|
+
target.url().startsWith('chrome-extension://'),
|
|
122
|
+
}),
|
|
64
123
|
};
|
|
65
124
|
if (options.wsEndpoint) {
|
|
66
125
|
connectOptions.browserWSEndpoint = options.wsEndpoint;
|
|
@@ -74,64 +133,59 @@ export async function ensureBrowserConnected(options) {
|
|
|
74
133
|
else if (channel || options.userDataDir) {
|
|
75
134
|
const userDataDir = options.userDataDir;
|
|
76
135
|
if (userDataDir) {
|
|
77
|
-
//
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const [rawPort, rawPath] = fileContent
|
|
82
|
-
.split('\n')
|
|
83
|
-
.map(line => {
|
|
84
|
-
return line.trim();
|
|
85
|
-
})
|
|
86
|
-
.filter(line => {
|
|
87
|
-
return !!line;
|
|
88
|
-
});
|
|
89
|
-
if (!rawPort || !rawPath) {
|
|
90
|
-
throw new Error(`Invalid DevToolsActivePort '${fileContent}' found`);
|
|
91
|
-
}
|
|
92
|
-
const port = parseInt(rawPort, 10);
|
|
93
|
-
if (isNaN(port) || port <= 0 || port > 65535) {
|
|
94
|
-
throw new Error(`Invalid port '${rawPort}' found`);
|
|
95
|
-
}
|
|
96
|
-
const browserWSEndpoint = `ws://127.0.0.1:${port}${rawPath}`;
|
|
97
|
-
connectOptions.browserWSEndpoint = browserWSEndpoint;
|
|
136
|
+
// Explicit user data dir provided
|
|
137
|
+
const wsEndpoint = readDevToolsActivePort(userDataDir);
|
|
138
|
+
if (wsEndpoint) {
|
|
139
|
+
connectOptions.browserWSEndpoint = wsEndpoint;
|
|
98
140
|
}
|
|
99
|
-
|
|
100
|
-
throw new Error(`Could not connect to Chrome in ${userDataDir}. Check if Chrome is running and remote debugging is enabled
|
|
101
|
-
cause: error,
|
|
102
|
-
});
|
|
141
|
+
else {
|
|
142
|
+
throw new Error(`Could not connect to Chrome in ${userDataDir}. Check if Chrome is running and remote debugging is enabled.`);
|
|
103
143
|
}
|
|
104
144
|
}
|
|
105
145
|
else {
|
|
106
146
|
if (!channel) {
|
|
107
147
|
throw new Error('Channel must be provided if userDataDir is missing');
|
|
108
148
|
}
|
|
109
|
-
//
|
|
149
|
+
// Collect candidate WebSocket endpoints from multiple directories.
|
|
150
|
+
// Try each one in order — stale DevToolsActivePort files are common,
|
|
151
|
+
// so we attempt the actual connection before moving to the next.
|
|
110
152
|
const profileDirName = channel && channel !== 'stable'
|
|
111
153
|
? `chrome-profile-${channel}`
|
|
112
154
|
: 'chrome-profile';
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
155
|
+
const mcpUserDataDir = path.join(os.homedir(), '.cache', 'chrome-devtools-mcp', profileDirName);
|
|
156
|
+
const chromeUserDataDir = getChromeDefaultUserDataDir(channel);
|
|
157
|
+
// Chrome's default profile is checked first — this is the user's
|
|
158
|
+
// real browser with remote debugging enabled via chrome://inspect.
|
|
159
|
+
// The MCP cache dir is checked second as a fallback for instances
|
|
160
|
+
// launched by the MCP server itself.
|
|
161
|
+
const candidates = [];
|
|
162
|
+
const chromeWsEndpoint = readDevToolsActivePort(chromeUserDataDir);
|
|
163
|
+
if (chromeWsEndpoint) {
|
|
164
|
+
candidates.push({ dir: chromeUserDataDir, wsEndpoint: chromeWsEndpoint });
|
|
165
|
+
}
|
|
166
|
+
const mcpWsEndpoint = readDevToolsActivePort(mcpUserDataDir);
|
|
167
|
+
if (mcpWsEndpoint) {
|
|
168
|
+
candidates.push({ dir: mcpUserDataDir, wsEndpoint: mcpWsEndpoint });
|
|
169
|
+
}
|
|
170
|
+
if (candidates.length === 0) {
|
|
171
|
+
throw new Error(`Could not connect to Chrome ${channel} channel. Checked ${mcpUserDataDir} and ${chromeUserDataDir}. Ensure Chrome is running with remote debugging enabled (chrome://inspect/#remote-debugging).`);
|
|
172
|
+
}
|
|
173
|
+
// Try each candidate endpoint, returning the first that connects
|
|
174
|
+
for (const candidate of candidates) {
|
|
175
|
+
try {
|
|
176
|
+
logger(`Trying DevToolsActivePort from ${candidate.dir}: ${candidate.wsEndpoint}`);
|
|
177
|
+
browser = await puppeteer.connect({
|
|
178
|
+
...connectOptions,
|
|
179
|
+
browserWSEndpoint: candidate.wsEndpoint,
|
|
180
|
+
});
|
|
181
|
+
logger(`Connected via ${candidate.dir}`);
|
|
182
|
+
return browser;
|
|
124
183
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
throw new Error(`Invalid port '${rawPort}' found`);
|
|
184
|
+
catch (err) {
|
|
185
|
+
logger(`Failed to connect via ${candidate.dir}: ${err.message}`);
|
|
128
186
|
}
|
|
129
|
-
const browserWSEndpoint = `ws://127.0.0.1:${port}${rawPath}`;
|
|
130
|
-
connectOptions.browserWSEndpoint = browserWSEndpoint;
|
|
131
|
-
}
|
|
132
|
-
catch (error) {
|
|
133
|
-
throw new Error(`Could not connect to Chrome ${channel} channel in ${derivedUserDataDir}. Check if Chrome is running and was launched with remote debugging enabled.`, { cause: error });
|
|
134
187
|
}
|
|
188
|
+
throw new Error(`Could not connect to Chrome ${channel} channel. Tried ${candidates.map(c => c.dir).join(' and ')}. Ensure Chrome is running with remote debugging enabled (chrome://inspect/#remote-debugging).`);
|
|
135
189
|
}
|
|
136
190
|
}
|
|
137
191
|
else {
|
|
@@ -201,7 +255,7 @@ export async function launch(options) {
|
|
|
201
255
|
try {
|
|
202
256
|
const browser = await puppeteer.launch({
|
|
203
257
|
channel: puppeteerChannel,
|
|
204
|
-
targetFilter: makeTargetFilter(),
|
|
258
|
+
targetFilter: makeTargetFilter(options.includeExtensionPages),
|
|
205
259
|
executablePath,
|
|
206
260
|
defaultViewport: null,
|
|
207
261
|
userDataDir,
|
|
@@ -210,6 +264,11 @@ export async function launch(options) {
|
|
|
210
264
|
args,
|
|
211
265
|
acceptInsecureCerts: options.acceptInsecureCerts,
|
|
212
266
|
handleDevToolsAsPage: true,
|
|
267
|
+
...(options.includeExtensionPages && {
|
|
268
|
+
enableExtensions: true,
|
|
269
|
+
isPageTargetCallback: (target) => target.type === 'page' ||
|
|
270
|
+
target.url?.startsWith('chrome-extension://') === true,
|
|
271
|
+
}),
|
|
213
272
|
});
|
|
214
273
|
if (options.logFile) {
|
|
215
274
|
// FIXME: we are probably subscribing too late to catch startup logs. We
|
|
@@ -219,7 +278,6 @@ export async function launch(options) {
|
|
|
219
278
|
}
|
|
220
279
|
if (options.viewport) {
|
|
221
280
|
const [page] = await browser.pages();
|
|
222
|
-
// @ts-expect-error internal API for now.
|
|
223
281
|
await page?.resize({
|
|
224
282
|
contentWidth: options.viewport.width,
|
|
225
283
|
contentHeight: options.viewport.height,
|
|
@@ -250,3 +308,51 @@ export async function ensureBrowserLaunched(options) {
|
|
|
250
308
|
browser = await launch(options);
|
|
251
309
|
return browser;
|
|
252
310
|
}
|
|
311
|
+
/**
|
|
312
|
+
* Disconnect from the current browser without killing the process.
|
|
313
|
+
* Clears the cached browser reference so future calls can connect to a new instance.
|
|
314
|
+
*/
|
|
315
|
+
export function disconnectBrowser() {
|
|
316
|
+
if (browser) {
|
|
317
|
+
try {
|
|
318
|
+
browser.disconnect();
|
|
319
|
+
}
|
|
320
|
+
catch {
|
|
321
|
+
// Ignore disconnect errors — browser may already be gone
|
|
322
|
+
}
|
|
323
|
+
browser = undefined;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Connect to a new browser instance, replacing the cached reference.
|
|
328
|
+
*
|
|
329
|
+
* @param options - Connection options (browserURL or wsEndpoint).
|
|
330
|
+
* @returns Connected browser instance.
|
|
331
|
+
*/
|
|
332
|
+
export async function connectToNewBrowser(options) {
|
|
333
|
+
const connectOptions = {
|
|
334
|
+
targetFilter: makeTargetFilter(options.includeExtensionPages),
|
|
335
|
+
defaultViewport: null,
|
|
336
|
+
handleDevToolsAsPage: true,
|
|
337
|
+
...(options.includeExtensionPages && {
|
|
338
|
+
isPageTargetCallback: (target) => target.type() === 'page' ||
|
|
339
|
+
target.url().startsWith('chrome-extension://'),
|
|
340
|
+
}),
|
|
341
|
+
};
|
|
342
|
+
if (options.wsEndpoint) {
|
|
343
|
+
connectOptions.browserWSEndpoint = options.wsEndpoint;
|
|
344
|
+
if (options.wsHeaders) {
|
|
345
|
+
connectOptions.headers = options.wsHeaders;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
else if (options.browserURL) {
|
|
349
|
+
connectOptions.browserURL = options.browserURL;
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
throw new Error('Either browserURL or wsEndpoint must be provided');
|
|
353
|
+
}
|
|
354
|
+
logger('Connecting Puppeteer to new browser:', JSON.stringify(connectOptions));
|
|
355
|
+
browser = await puppeteer.connect(connectOptions);
|
|
356
|
+
logger('Connected to new browser');
|
|
357
|
+
return browser;
|
|
358
|
+
}
|
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))
|
|
@@ -170,11 +175,12 @@ export function parseArguments(version, argv = process.argv) {
|
|
|
170
175
|
.check(args => {
|
|
171
176
|
// We can't set default in the options else
|
|
172
177
|
// Yargs will complain
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
args.
|
|
178
|
+
// Note: Use explicit undefined checks since empty strings are valid falsy values
|
|
179
|
+
if (args.channel === undefined &&
|
|
180
|
+
args.browserUrl === undefined &&
|
|
181
|
+
args.wsEndpoint === undefined &&
|
|
182
|
+
args.executablePath === undefined) {
|
|
183
|
+
args.channel = 'stable';
|
|
178
184
|
}
|
|
179
185
|
return true;
|
|
180
186
|
})
|