@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.
- package/README.md +120 -10
- package/build/src/McpContext.js +50 -3
- package/build/src/browser.js +62 -6
- package/build/src/cli.js +6 -1
- 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/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 +0 -1
- package/build/src/tools/tools.js +4 -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 +17 -10
- 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
|
@@ -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
|
*/
|
package/build/src/browser.js
CHANGED
|
@@ -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 = '
|
|
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
|
+
}
|