brave-mcp 1.11.0 → 1.12.0
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 +69 -1
- package/{build → out/Default/gen}/src/BrowserManager.js +28 -18
- package/out/Default/gen/src/LogpointManager.js +307 -0
- package/{build → out/Default/gen}/src/McpContext.js +435 -87
- package/{build → out/Default/gen}/src/McpPage.js +326 -58
- package/{build → out/Default/gen}/src/McpResponse.js +89 -46
- package/{build → out/Default/gen}/src/TextSnapshot.js +37 -18
- package/out/Default/gen/src/ToolHandler.js +492 -0
- package/{build → out/Default/gen}/src/bin/brave-devtools-mcp-main.js +6 -4
- package/{build → out/Default/gen}/src/bin/brave-devtools.js +4 -4
- package/{build → out/Default/gen}/src/collectors/PageCollector.js +28 -12
- package/{build → out/Default/gen}/src/collectors/ServiceWorkerCollector.js +2 -2
- package/out/Default/gen/src/collectors/constants.js +7 -0
- package/out/Default/gen/src/config/ConfigParser.js +162 -0
- package/{build → out/Default/gen}/src/config/ConfigWatcher.js +3 -2
- package/out/Default/gen/src/config/ServerConfig.js +251 -0
- package/{build → out/Default/gen}/src/config/browser-options.js +13 -4
- package/{build → out/Default/gen}/src/config/category-options.js +4 -0
- package/{build → out/Default/gen}/src/config/cli-options.js +113 -20
- package/{build → out/Default/gen}/src/config/mcp-options.js +62 -36
- package/{build → out/Default/gen}/src/config/puppeteer-options.js +4 -2
- package/{build → out/Default/gen}/src/daemon/client.js +3 -3
- package/{build → out/Default/gen}/src/daemon/daemon.js +7 -3
- package/{build → out/Default/gen}/src/daemon/utils.js +2 -1
- package/{build → out/Default/gen}/src/devtools/DevToolsCommentBridge.js +28 -5
- package/{build → out/Default/gen}/src/devtools/DevtoolsUtils.js +94 -20
- package/{build → out/Default/gen}/src/formatters/ConsoleFormatter.js +28 -1
- package/{build → out/Default/gen}/src/formatters/HeapSnapshotFormatter.js +14 -18
- package/out/Default/gen/src/formatters/LogpointFormatter.js +62 -0
- package/{build → out/Default/gen}/src/formatters/NetworkFormatter.js +70 -17
- package/out/Default/gen/src/formatters/RemoteObjectFormatter.js +231 -0
- package/{build → out/Default/gen}/src/formatters/SnapshotFormatter.js +13 -3
- package/out/Default/gen/src/formatters/WebAuthnFormatter.js +56 -0
- package/out/Default/gen/src/index.js +434 -0
- package/out/Default/gen/src/processors/HeapSnapshotManager.js +569 -0
- package/{build → out/Default/gen}/src/telemetry/transformation.js +1 -1
- package/{build → out/Default/gen}/src/third_party/THIRD_PARTY_NOTICES +752 -716
- package/out/Default/gen/src/third_party/bundled-packages.json +11 -0
- package/out/Default/gen/src/third_party/cli.js +9239 -0
- package/out/Default/gen/src/third_party/devtools-formatter-worker.js +17825 -0
- package/out/Default/gen/src/third_party/devtools-heap-snapshot-worker.js +12575 -0
- package/{build → out/Default/gen}/src/third_party/index.js +70243 -60439
- package/out/Default/gen/src/third_party/lighthouse-devtools-mcp-bundle.js +60395 -0
- package/out/Default/gen/src/tools/ToolDefinition.js +180 -0
- package/{build → out/Default/gen}/src/tools/categories.js +2 -0
- package/{build → out/Default/gen}/src/tools/comments.js +42 -3
- package/{build → out/Default/gen}/src/tools/console.js +8 -6
- package/{build → out/Default/gen}/src/tools/css.js +1 -0
- package/{build → out/Default/gen}/src/tools/emulation.js +5 -4
- package/{build → out/Default/gen}/src/tools/extensions.js +5 -0
- package/{build → out/Default/gen}/src/tools/input.js +22 -10
- package/{build → out/Default/gen}/src/tools/lighthouse.js +23 -7
- package/out/Default/gen/src/tools/logpoints.js +51 -0
- package/{build → out/Default/gen}/src/tools/memory.js +59 -53
- package/{build → out/Default/gen}/src/tools/network.js +5 -4
- package/{build → out/Default/gen}/src/tools/pages.js +48 -6
- package/{build → out/Default/gen}/src/tools/performance.js +21 -5
- package/{build → out/Default/gen}/src/tools/pwa.js +7 -0
- package/{build → out/Default/gen}/src/tools/screencast.js +3 -1
- package/{build → out/Default/gen}/src/tools/screenshot.js +23 -7
- package/{build → out/Default/gen}/src/tools/script.js +38 -6
- package/{build → out/Default/gen}/src/tools/slim/tools.js +21 -5
- package/{build → out/Default/gen}/src/tools/snapshot.js +29 -5
- package/{build → out/Default/gen}/src/tools/thirdPartyDeveloper.js +6 -20
- package/out/Default/gen/src/tools/toolNames.js +10 -0
- package/{build → out/Default/gen}/src/tools/tools.js +4 -0
- package/out/Default/gen/src/tools/webauthn.js +51 -0
- package/{build → out/Default/gen}/src/tools/webmcp.js +5 -19
- package/out/Default/gen/src/utils/TaskRunner.js +136 -0
- package/{build → out/Default/gen}/src/utils/WaitForHelper.js +1 -1
- package/{build → out/Default/gen}/src/utils/bytes.js +2 -12
- package/{build → out/Default/gen}/src/utils/check-for-updates.js +1 -1
- package/out/Default/gen/src/utils/disclaimers.js +25 -0
- package/{build → out/Default/gen}/src/utils/errorHandling.js +3 -0
- package/{build → out/Default/gen}/src/utils/keyboard.js +15 -5
- package/out/Default/gen/src/utils/logger.js +131 -0
- package/out/Default/gen/src/utils/resourceType.js +27 -0
- package/out/Default/gen/src/utils/text.js +44 -0
- package/out/Default/gen/src/utils/timeout.js +14 -0
- package/{build → out/Default/gen}/src/utils/url.js +41 -0
- package/{build → out/Default/gen}/src/version.js +1 -1
- package/package.json +26 -21
- package/skills/a11y-debugging/SKILL.md +3 -8
- package/skills/brave-devtools/SKILL.md +1 -0
- package/build/src/ToolHandler.js +0 -369
- package/build/src/config/ConfigParser.js +0 -264
- package/build/src/index.js +0 -276
- package/build/src/processors/HeapSnapshotManager.js +0 -307
- package/build/src/third_party/bundled-packages.json +0 -11
- package/build/src/third_party/devtools-formatter-worker.js +0 -16625
- package/build/src/third_party/devtools-heap-snapshot-worker.js +0 -12084
- package/build/src/third_party/lighthouse-devtools-mcp-bundle.js +0 -58435
- package/build/src/tools/ToolDefinition.js +0 -90
- package/build/src/utils/logger.js +0 -62
- package/build/src/utils/text.js +0 -15
- /package/{build → out/Default/gen}/src/McpWorker.js +0 -0
- /package/{build → out/Default/gen}/src/SlimMcpResponse.js +0 -0
- /package/{build → out/Default/gen}/src/bin/brave-devtools-mcp.js +0 -0
- /package/{build → out/Default/gen}/src/bin/check-latest-version.js +0 -0
- /package/{build → out/Default/gen}/src/config/ConfigLocator.js +0 -0
- /package/{build → out/Default/gen}/src/config/cli-commands.js +0 -0
- /package/{build → out/Default/gen}/src/config/tool-options.js +0 -0
- /package/{build → out/Default/gen}/src/daemon/types.js +0 -0
- /package/{build → out/Default/gen}/src/devtools/McpHostBindingAdapter.js +0 -0
- /package/{build → out/Default/gen}/src/devtools/issueDescriptions.js +0 -0
- /package/{build → out/Default/gen}/src/formatters/CommentFormatter.js +0 -0
- /package/{build → out/Default/gen}/src/formatters/CssFormatter.js +0 -0
- /package/{build → out/Default/gen}/src/formatters/IssueFormatter.js +0 -0
- /package/{build → out/Default/gen}/src/processors/ChunkedTraceParser.js +0 -0
- /package/{build → out/Default/gen}/src/processors/PerformanceTrace.js +0 -0
- /package/{build → out/Default/gen}/src/telemetry/ClearcutLogger.js +0 -0
- /package/{build → out/Default/gen}/src/telemetry/WatchdogClient.js +0 -0
- /package/{build → out/Default/gen}/src/telemetry/errors.js +0 -0
- /package/{build → out/Default/gen}/src/telemetry/flagUtils.js +0 -0
- /package/{build → out/Default/gen}/src/telemetry/metricsRegistry.js +0 -0
- /package/{build → out/Default/gen}/src/telemetry/persistence.js +0 -0
- /package/{build → out/Default/gen}/src/telemetry/types.js +0 -0
- /package/{build → out/Default/gen}/src/telemetry/watchdog/ClearcutSender.js +0 -0
- /package/{build → out/Default/gen}/src/telemetry/watchdog/main.js +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/CoepCoopSandboxedIframeCannotNavigateToCoopPage.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/CoepCorpNotSameOrigin.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/CoepCorpNotSameOriginAfterDefaultedToSameOriginByCoep.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/CoepCorpNotSameSite.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/CoepFrameResourceNeedsCoepHeader.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/CompatibilityModeQuirks.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/CookieAttributeValueExceedsMaxSize.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/LowTextContrast.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorRead.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorSet.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnRead.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnSet.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeRead.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeSet.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/bounceTrackingMitigations.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/clientHintMetaTagAllowListInvalidOrigin.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/clientHintMetaTagModifiedHTML.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/connectionAllowlistEmbeddingRequirementNotSatisfied.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/connectionAllowlistIFrameAttributeLoosensEmbeddingRequirement.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowConnectionAllowlistFrom.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowlistItemType.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/connectionAllowlistInvalidHeader.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/connectionAllowlistInvalidUrlPattern.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/connectionAllowlistItemNotInnerList.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/connectionAllowlistMoreThanOneList.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/connectionAllowlistReportingEndpointNotToken.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/cookieCrossSiteRedirectDowngrade.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/cookieExcludeDomainNonAscii.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/cookieExcludePortMismatch.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/cookieExcludeSchemeMismatch.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutRead.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutSet.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/cookieWarnDomainNonAscii.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/cookieWarnMetadataGrantRead.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/cookieWarnMetadataGrantReadOptOut.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/cookieWarnMetadataGrantSet.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/cookieWarnMetadataGrantSetOptOut.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutRead.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutSet.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/corsAllowCredentialsRequired.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/corsDisabledScheme.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/corsDisallowedByMode.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/corsHeaderDisallowedByPreflightResponse.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/corsInvalidHeaderValues.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/corsLocalNetworkAccessPermissionDenied.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/corsMethodDisallowedByPreflightResponse.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/corsNoCorsRedirectModeNotFollow.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/corsOriginMismatch.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/corsPreflightResponseInvalid.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/corsRedirectContainsCredentials.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/corsWildcardOriginNotAllowed.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/cspEvalViolation.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/cspInlineViolation.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/cspTrustedTypesPolicyViolation.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/cspTrustedTypesSinkViolation.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/cspURLViolation.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/deprecation.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestAccountsEmptyList.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestAccountsHttpNotFound.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidContentType.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidResponse.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestAccountsNoResponse.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestDnsFetchFailed.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestDnsInvalidRecord.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownHttpNotFound.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidContentType.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidResponse.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownNoResponse.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestInvalidEmail.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestJwksHttpNotFound.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestJwksInvalidResponse.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestKeyBindingSigningFailed.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestRpOriginIsOpaque.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenHttpNotFound.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidContentType.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidResponse.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidSdJwt.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenMalformedSdJwt.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenNoResponse.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidAudience.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidIssuedAt.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidNonce.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidSdHash.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidTyp.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingAud.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingCnf.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingIat.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingNonce.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingSdHash.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbSignatureFailed.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmail.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmailVerified.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidHolderKey.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuedAt.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuer.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtJwksMissingKeys.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingCnf.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingEmail.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIat.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIss.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtSignatureFailed.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtUnsupportedHeaderAlg.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestUserLoggedOut.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestWellKnownAccountsEndpointCrossOrigin.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestWellKnownHttpNotFound.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidContentType.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidResponse.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestWellKnownIssuanceEndpointCrossOrigin.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestWellKnownListEmpty.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingAccountsEndpoint.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingIssuanceEndpoint.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestWellKnownNoResponse.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/emailVerificationRequestWellKnownUnsupportedSigningAlgorithm.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthRequestAccountsBlockedByConnectionAllowlist.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthRequestAccountsHttpNotFound.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthRequestAccountsInvalidResponse.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthRequestAccountsNoResponse.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthRequestApprovalDeclined.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthRequestCanceled.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthRequestConfigBlockedByConnectionAllowlist.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthRequestErrorFetchingSignin.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthRequestErrorIdToken.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthRequestIdTokenBlockedByConnectionAllowlist.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthRequestIdTokenHttpNotFound.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidRequest.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidResponse.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthRequestIdTokenNoResponse.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthRequestInvalidSigninResponse.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthRequestManifestHttpNotFound.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthRequestManifestInvalidResponse.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthRequestManifestNoResponse.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthRequestTooManyRequests.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthRequestWellKnownBlockedByConnectionAllowlist.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidAccountsResponse.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidConfigOrWellKnown.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoAccountSharingPermission.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoApiPermission.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoReturningUserFromFetchedAccounts.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotIframe.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotPotentiallyTrustworthy.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSameOrigin.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSignedInWithIdp.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/fetchingPartitionedBlobURL.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/genericBackUINavigationWouldSkipAd.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/genericFormAriaLabelledByToNonExistingIdError.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/genericFormAutocompleteAttributeEmptyError.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/genericFormDuplicateIdForInputError.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/genericFormEmptyIdAndNameAttributesForInputError.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/genericFormInputAssignedAutocompleteValueToIdOrNameAttributeError.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/genericFormInputHasWrongButWellIntendedAutocompleteValueError.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/genericFormInputWithNoLabelError.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/genericFormLabelForMatchesNonExistingIdError.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/genericFormLabelForNameError.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/genericFormLabelHasNeitherForNorNestedInputError.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/genericFormModelContextMissingToolDescription.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/genericFormModelContextMissingToolName.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/genericFormModelContextParameterMissingName.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/genericFormModelContextParameterMissingTitleAndDescription.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/genericFormModelContextRequiredParameterMissingName.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/genericGeolocationPromptWithoutUserGesture.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/genericNavigationEntryMarkedSkippable.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/genericResponseWasBlockedByORB.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/heavyAd.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/lazyLoadImageZeroSize.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/mixedContent.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/navigatingPartitionedBlobURL.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementActivationDisabled.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluder.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluderParent.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementCspFrameAncestorsMissing.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementFencedFrameDisallowed.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementFontSizeTooLarge.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementFontSizeTooSmall.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementGeolocationDeprecated.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementInsetBoxShadowUnsupported.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementInvalidDisplayStyle.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementInvalidSizeValue.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementInvalidType.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementInvalidTypeActivation.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementLowContrast.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementNonOpaqueColor.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementPaddingBottomUnsupported.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementPaddingRightUnsupported.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementPermissionsPolicyBlocked.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementRegistrationFailed.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementRequestInProgress.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementSecurityChecksFailed.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementTypeNotSupported.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/permissionElementUntrustedEvent.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/placeholderDescriptionForInvisibleIssues.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/propertyRuleInvalidNameIssue.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/propertyRuleIssue.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedOptGroupChild.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedSelectChild.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentAttributesSelectDescendant.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentLegendChild.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentOptionChild.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/selectElementAccessibilityNonPhrasingContentOptionChild.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/selectivePermissionsIntervention.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedArrayBuffer.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryUseErrorCrossOriginNoCorsRequest.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryUseErrorDictionaryLoadFailure.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryUseErrorMatchingDictionaryNotUsed.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryUseErrorUnexpectedContentDictionaryHeader.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryWriteErrorCossOriginNoCorsRequest.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryWriteErrorDisallowedBySettings.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryWriteErrorExpiredResponse.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryWriteErrorFeatureDisabled.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInsufficientResources.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidMatchField.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidStructuredHeader.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidTTLField.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNavigationRequest.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNoMatchField.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonIntegerTTLField.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonListMatchDestField.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonSecureContext.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringIdField.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringInMatchDestList.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringMatchField.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonTokenTypeField.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryWriteErrorRequestAborted.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryWriteErrorShuttingDown.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryWriteErrorTooLongIdField.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sharedDictionaryWriteErrorUnsupportedType.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sriInvalidSignatureHeader.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sriInvalidSignatureInputHeader.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sriMissingSignatureHeader.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sriMissingSignatureInputHeader.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sriSignatureHeaderValueIsIncorrectLength.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sriSignatureHeaderValueIsNotByteSequence.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sriSignatureHeaderValueIsParameterized.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentName.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentType.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidDerivedComponentParameter.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidHeaderComponentParameter.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidParameter.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sriSignatureInputHeaderKeyIdLength.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingLabel.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingRequiredParameters.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sriSignatureInputHeaderValueMissingComponents.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sriSignatureInputHeaderValueNotInnerList.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sriValidationFailedIntegrityMismatch.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sriValidationFailedInvalidLength.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sriValidationFailedSignatureExpired.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/sriValidationFailedSignatureMismatch.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/stylesheetLateImport.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/stylesheetRequestFailed.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/summaryElementAccessibilityInteractiveContentSummaryDescendant.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestLength.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestType.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/unencodedDigestMalformedDictionary.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/unencodedDigestUnknownAlgorithm.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/webInstallManifestMissingId.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/webInstallManifestMissingNameOrShortName.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/webInstallManifestParsingOrNetworkError.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/webInstallNoManifest.md +0 -0
- /package/{build → out/Default/gen}/src/third_party/issue-descriptions/webInstallStartUrlInvalid.md +0 -0
- /package/{build → out/Default/gen}/src/types.js +0 -0
- /package/{build → out/Default/gen}/src/utils/files.js +0 -0
- /package/{build → out/Default/gen}/src/utils/id.js +0 -0
- /package/{build → out/Default/gen}/src/utils/pagination.js +0 -0
- /package/{build → out/Default/gen}/src/utils/polyfill.js +0 -0
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@ When the agent connects, Brave asks for permission. Click **Allow**.
|
|
|
62
62
|
|
|
63
63
|
Use [Chrome DevTools MCP](https://github.com/ChromeDevTools/chrome-devtools-mcp) when your target browser is Chrome. Use `brave-mcp` when your target browser is Brave and you want Brave-native discovery, profiles, channels, naming, and privacy defaults without giving up upstream features.
|
|
64
64
|
|
|
65
|
-
## [Tool reference](./docs/tool-reference.md) | [Changelog](./CHANGELOG.md) | [Contributing](./CONTRIBUTING.md) | [Troubleshooting](./docs/troubleshooting.md) | [Design principles](./docs/design-principles.md)
|
|
65
|
+
## [Tool reference](./docs/tool-reference.md) | [CLI](./docs/cli.md) | [Skills](#skills) | [Changelog](./CHANGELOG.md) | [Contributing](./CONTRIBUTING.md) | [Troubleshooting](./docs/troubleshooting.md) | [Design principles](./docs/design-principles.md)
|
|
66
66
|
|
|
67
67
|
## Key features
|
|
68
68
|
|
|
@@ -163,6 +163,70 @@ Brave asks you to allow the connection. After you click **Allow**, your MCP clie
|
|
|
163
163
|
> [!NOTE]
|
|
164
164
|
> The MCP server connects to Brave only when the MCP client first uses a tool that needs a browser, not when the client starts. Without `--autoConnect`, that is when the server launches its own Brave.
|
|
165
165
|
|
|
166
|
+
## CLI
|
|
167
|
+
|
|
168
|
+
The package also includes the `brave-devtools` CLI. Use it to control the browser from your terminal without MCP:
|
|
169
|
+
|
|
170
|
+
```sh
|
|
171
|
+
npm i brave-mcp@latest -g
|
|
172
|
+
brave-devtools navigate_page 1 --url "https://brave.com"
|
|
173
|
+
brave-devtools take_screenshot 1 --filePath screenshot.png
|
|
174
|
+
brave-devtools stop
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
See the [CLI guide][cli] for all commands and options.
|
|
178
|
+
|
|
179
|
+
## Skills
|
|
180
|
+
|
|
181
|
+
The package includes [skills](./skills) that teach your agent how to use Brave DevTools:
|
|
182
|
+
|
|
183
|
+
| Skill | Use it to |
|
|
184
|
+
| --------------------------------------------------------- | ---------------------------------------------- |
|
|
185
|
+
| [`brave-devtools`](./skills/brave-devtools) | Debug and automate pages with the MCP tools. |
|
|
186
|
+
| [`brave-devtools-cli`](./skills/brave-devtools-cli) | Run the CLI and write shell scripts with it. |
|
|
187
|
+
| [`a11y-debugging`](./skills/a11y-debugging) | Audit and fix accessibility issues. |
|
|
188
|
+
| [`cookie-debugging`](./skills/cookie-debugging) | Debug cookies, sessions, and authentication. |
|
|
189
|
+
| [`debug-optimize-lcp`](./skills/debug-optimize-lcp) | Debug and improve Largest Contentful Paint. |
|
|
190
|
+
| [`memory-leak-debugging`](./skills/memory-leak-debugging) | Find and fix memory leaks. |
|
|
191
|
+
| [`troubleshooting`](./skills/troubleshooting) | Fix connection problems between MCP and Brave. |
|
|
192
|
+
|
|
193
|
+
### Install skills as a plugin
|
|
194
|
+
|
|
195
|
+
A plugin installs the MCP server and all skills, including the CLI skill:
|
|
196
|
+
|
|
197
|
+
- **Claude Code:** Run `/plugin marketplace add triuzzi/brave-devtools-mcp`, then `/plugin install brave-devtools-mcp@brave-devtools-plugins`.
|
|
198
|
+
- **Copilot / VS Code:** Run **Chat: Install Plugin From Source** and enter `triuzzi/brave-devtools-mcp`.
|
|
199
|
+
- **Gemini CLI:** Run `gemini extensions install --auto-update https://github.com/triuzzi/brave-devtools-mcp`.
|
|
200
|
+
|
|
201
|
+
See the [Client Configurations Guide][client-configurations-guide] for more information.
|
|
202
|
+
|
|
203
|
+
### Install skills manually
|
|
204
|
+
|
|
205
|
+
If your agent does not support plugins, copy the skill folders into the skills directory of your agent. The npm package contains the skills:
|
|
206
|
+
|
|
207
|
+
```sh
|
|
208
|
+
npm i brave-mcp@latest -g
|
|
209
|
+
# Example for Claude Code. Use the skills directory of your agent.
|
|
210
|
+
mkdir -p ~/.claude/skills
|
|
211
|
+
cp -R "$(npm root -g)/brave-mcp/skills/brave-devtools-cli" ~/.claude/skills/
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
> [!IMPORTANT]
|
|
215
|
+
> The `brave-devtools-cli` skill requires the CLI. Install the package globally before you use the skill.
|
|
216
|
+
|
|
217
|
+
### Keep skills up to date
|
|
218
|
+
|
|
219
|
+
Skills describe the tools of a specific version. Update the skills each time you update the MCP server or the CLI. Outdated skills can tell your agent to use tools or parameters that changed.
|
|
220
|
+
|
|
221
|
+
- **Plugins:** Use the update command of the plugin manager of your agent. The Gemini CLI command above enables automatic updates with `--auto-update`.
|
|
222
|
+
- **Manual installs:** Update the package, then copy the skill folders again:
|
|
223
|
+
|
|
224
|
+
```sh
|
|
225
|
+
npm i brave-mcp@latest -g
|
|
226
|
+
mkdir -p ~/.claude/skills
|
|
227
|
+
cp -R "$(npm root -g)/brave-mcp/skills/brave-devtools-cli" ~/.claude/skills/
|
|
228
|
+
```
|
|
229
|
+
|
|
166
230
|
## Tools
|
|
167
231
|
|
|
168
232
|
If you run into any issues, checkout our [troubleshooting guide][troubleshooting].
|
|
@@ -172,6 +236,8 @@ See the full [Tool reference][tool-reference] for a complete list of all support
|
|
|
172
236
|
|
|
173
237
|
Find the complete list of server parameters (e.g., `--headless`, `--isolated`, `--slim`) and how to configure WebSocket connections in the [Configuration Guide][configuration-guide].
|
|
174
238
|
|
|
239
|
+
For basic browser tasks only, the `--slim` flag exposes a reduced set of tools. See the [Slim tool reference](./docs/slim-tool-reference.md).
|
|
240
|
+
|
|
175
241
|
## Advanced usage
|
|
176
242
|
|
|
177
243
|
For advanced features such as handling concurrent sessions, persistent user data directories, connecting to a running Brave instance instead of starting a new one, or debugging on Android, see our [Advanced Usage Guide][advanced-usage-guide].
|
|
@@ -187,6 +253,8 @@ If you are developing agentic tooling and want to provide an integrated browser
|
|
|
187
253
|
For a reference implementation, see the [Gemini CLI browser agent documentation](https://geminicli.com/docs/core/subagents/#browser-agent).
|
|
188
254
|
|
|
189
255
|
[advanced-usage-guide]: ./docs/advanced-usage.md
|
|
256
|
+
[cli]: ./docs/cli.md
|
|
257
|
+
[skills]: #skills
|
|
190
258
|
[client-configurations-guide]: ./docs/client-configurations.md
|
|
191
259
|
[configuration-guide]: ./docs/configuration.md
|
|
192
260
|
[tool-reference]: ./docs/tool-reference.md
|
|
@@ -60,8 +60,8 @@ import { execSync } from 'node:child_process';
|
|
|
60
60
|
import fs from 'node:fs';
|
|
61
61
|
import os from 'node:os';
|
|
62
62
|
import path from 'node:path';
|
|
63
|
-
import { Mutex, puppeteer } from './third_party/index.js';
|
|
64
|
-
import { logger, puppeteerLogger } from './utils/logger.js';
|
|
63
|
+
import { Mutex, puppeteer, } from './third_party/index.js';
|
|
64
|
+
import { logger, puppeteerLogger, redactConnectOptionsForLogging, } from './utils/logger.js';
|
|
65
65
|
import { isAllowedUrl } from './utils/url.js';
|
|
66
66
|
// Node runs a timer whose delay exceeds 2^31-1 ms after 1 ms instead, which
|
|
67
67
|
// would make every CDP call and tool call time out at once.
|
|
@@ -177,11 +177,11 @@ export class BrowserManager {
|
|
|
177
177
|
#initPromise;
|
|
178
178
|
#mutex = new Mutex();
|
|
179
179
|
#closingCount = 0;
|
|
180
|
-
#
|
|
180
|
+
#config;
|
|
181
181
|
#options;
|
|
182
182
|
#browserAttempt = new BrowserAttempt();
|
|
183
|
-
constructor(
|
|
184
|
-
this.#
|
|
183
|
+
constructor(config, options = {}) {
|
|
184
|
+
this.#config = config;
|
|
185
185
|
this.#options = options;
|
|
186
186
|
}
|
|
187
187
|
static makeTargetFilter(enableExtensions = false) {
|
|
@@ -308,8 +308,9 @@ export class BrowserManager {
|
|
|
308
308
|
* McpContext initialization built on it — if ensureBrowser() already
|
|
309
309
|
* resolved and it's that later step hanging (e.g. a dead CDP transport),
|
|
310
310
|
* the token rotation alone does nothing, since #browser is already
|
|
311
|
-
* cached. Safe to forget unconditionally here:
|
|
312
|
-
*
|
|
311
|
+
* cached. Safe to forget unconditionally here: a tool call that already
|
|
312
|
+
* received a Browser keeps using it, so forgetting the cached value only
|
|
313
|
+
* makes the next ensureBrowser() reconnect.
|
|
313
314
|
*/
|
|
314
315
|
abandonPendingAttempt() {
|
|
315
316
|
this.#browserAttempt = new BrowserAttempt();
|
|
@@ -317,27 +318,30 @@ export class BrowserManager {
|
|
|
317
318
|
this.#forgetCurrentBrowser();
|
|
318
319
|
}
|
|
319
320
|
async #initBrowser() {
|
|
320
|
-
if (this.#
|
|
321
|
-
this.#
|
|
322
|
-
this.#
|
|
321
|
+
if (this.#config.resolved.browserUrl ||
|
|
322
|
+
this.#config.resolved.wsEndpoint ||
|
|
323
|
+
this.#config.resolved.autoConnect) {
|
|
323
324
|
return await this.#connect();
|
|
324
325
|
}
|
|
325
326
|
return await this.#launch();
|
|
326
327
|
}
|
|
327
328
|
async #launch() {
|
|
328
|
-
const { channel, headless, isolated = false, categoryExtensions: enableExtensions, viaCli, viewport, acceptInsecureCerts, experimentalDevtools: devtools = false, blockedUrlPattern: blocklist, allowedUrlPattern: allowlist, ignoreDefaultBraveArg, proxyServer, } = this.#
|
|
329
|
+
const { channel, headless, isolated = false, categoryExtensions: enableExtensions, viaCli, viewport, acceptInsecureCerts, experimentalDevtools: devtools = false, blockedUrlPattern: blocklist, allowedUrlPattern: allowlist, ignoreDefaultBraveArg, proxyServer, } = this.#config.resolved;
|
|
329
330
|
const profileDirName = channel && channel !== 'release'
|
|
330
331
|
? `brave-profile-${channel}`
|
|
331
332
|
: 'brave-profile';
|
|
332
|
-
const executablePath = this.#
|
|
333
|
-
|
|
333
|
+
const executablePath = this.#config.resolved.executablePath ??
|
|
334
|
+
resolveBraveExecutablePath(channel);
|
|
335
|
+
let userDataDir = this.#config.resolved.userDataDir;
|
|
334
336
|
if (!isolated && !userDataDir) {
|
|
335
337
|
userDataDir = path.join(os.homedir(), '.cache', viaCli ? 'brave-devtools-mcp-cli' : 'brave-devtools-mcp', profileDirName);
|
|
336
338
|
await fs.promises.mkdir(userDataDir, {
|
|
337
339
|
recursive: true,
|
|
338
340
|
});
|
|
339
341
|
}
|
|
340
|
-
const args = [
|
|
342
|
+
const args = [
|
|
343
|
+
...(this.#config.resolved.braveArg ?? []),
|
|
344
|
+
];
|
|
341
345
|
if (proxyServer) {
|
|
342
346
|
args.push(`--proxy-server=${proxyServer}`);
|
|
343
347
|
}
|
|
@@ -370,6 +374,12 @@ export class BrowserManager {
|
|
|
370
374
|
blocklist,
|
|
371
375
|
allowlist,
|
|
372
376
|
logger: puppeteerLogger,
|
|
377
|
+
// The entry points own signal handling and call close(), which
|
|
378
|
+
// closes the browser gracefully. Puppeteer's handlers would SIGKILL
|
|
379
|
+
// Chrome synchronously on the same signal and win the race.
|
|
380
|
+
handleSIGINT: false,
|
|
381
|
+
handleSIGTERM: false,
|
|
382
|
+
handleSIGHUP: false,
|
|
373
383
|
});
|
|
374
384
|
if (this.#options.logFile) {
|
|
375
385
|
// FIXME: we are probably subscribing too late to catch startup logs. We
|
|
@@ -411,9 +421,9 @@ export class BrowserManager {
|
|
|
411
421
|
}
|
|
412
422
|
}
|
|
413
423
|
async #connect() {
|
|
414
|
-
const { browserUrl: browserURL, wsEndpoint, wsHeaders, autoConnect: isAutoConnect, userDataDir, blockedUrlPattern: blocklist, allowedUrlPattern: allowlist, categoryExtensions: enableExtensions, } = this.#
|
|
424
|
+
const { browserUrl: browserURL, wsEndpoint, wsHeaders, autoConnect: isAutoConnect, userDataDir, blockedUrlPattern: blocklist, allowedUrlPattern: allowlist, categoryExtensions: enableExtensions, } = this.#config.resolved;
|
|
415
425
|
// Important: only pass channel, if autoConnect is true.
|
|
416
|
-
const channel = isAutoConnect ? this.#
|
|
426
|
+
const channel = isAutoConnect ? this.#config.resolved.channel : undefined;
|
|
417
427
|
const connectOptions = {
|
|
418
428
|
targetFilter: _a.makeTargetFilter(enableExtensions),
|
|
419
429
|
defaultViewport: null,
|
|
@@ -427,7 +437,7 @@ export class BrowserManager {
|
|
|
427
437
|
// periodically and terminates it on a missed pong, which rejects the
|
|
428
438
|
// in-flight CDP calls and fires `disconnected` so #evictIfCurrent()
|
|
429
439
|
// drops the stale handle. Without this, such a call would hang until
|
|
430
|
-
// protocolTimeout
|
|
440
|
+
// protocolTimeout inside the context queue.
|
|
431
441
|
wsOptions: {
|
|
432
442
|
keepAlive: true,
|
|
433
443
|
headers: wsHeaders,
|
|
@@ -475,7 +485,7 @@ export class BrowserManager {
|
|
|
475
485
|
else {
|
|
476
486
|
throw new Error('Either browserURL, wsEndpoint, channel or userDataDir must be provided');
|
|
477
487
|
}
|
|
478
|
-
logger?.('Connecting Puppeteer to ', JSON.stringify(connectOptions));
|
|
488
|
+
logger?.('Connecting Puppeteer to ', JSON.stringify(redactConnectOptionsForLogging(connectOptions)));
|
|
479
489
|
try {
|
|
480
490
|
const connected = await puppeteer.connect(connectOptions);
|
|
481
491
|
logger?.('Connected Puppeteer');
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { Script } from 'node:vm';
|
|
7
|
+
import { waitForSourceMaps, } from './devtools/DevtoolsUtils.js';
|
|
8
|
+
import { DevTools } from './third_party/index.js';
|
|
9
|
+
/**
|
|
10
|
+
* Thrown by {@link LogpointManager.setLogpoints} when some locations could
|
|
11
|
+
* not be bound. The other logpoints were applied; `logpoints` holds the
|
|
12
|
+
* resulting state.
|
|
13
|
+
*/
|
|
14
|
+
export class LogpointError extends Error {
|
|
15
|
+
logpoints;
|
|
16
|
+
constructor(message, logpoints) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = 'LogpointError';
|
|
19
|
+
this.logpoints = logpoints;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export const LOGPOINT_FORMAT = '<url>:<lineNumber>[:<columnNumber>] <expression>';
|
|
23
|
+
/**
|
|
24
|
+
* Parses a logpoint given as `<url>:<lineNumber>[:<columnNumber>] <expression>`.
|
|
25
|
+
* The url may itself contain colons (for example a port), so the location is
|
|
26
|
+
* the shortest prefix that ends in one or two numbers followed by whitespace.
|
|
27
|
+
*/
|
|
28
|
+
export function parseLogpoint(text) {
|
|
29
|
+
const match = /^(\S+?):(\d+)(?::(\d+))?\s+(\S.*)$/s.exec(text.trim());
|
|
30
|
+
if (!match) {
|
|
31
|
+
throw new Error(`Invalid logpoint "${text}". Expected the format ${LOGPOINT_FORMAT}, for example "src/app.ts:42 'total', cart.total".`);
|
|
32
|
+
}
|
|
33
|
+
const [, url, lineNumber, columnNumber, expression] = match;
|
|
34
|
+
const logpoint = {
|
|
35
|
+
url,
|
|
36
|
+
lineNumber: Number(lineNumber),
|
|
37
|
+
expression,
|
|
38
|
+
};
|
|
39
|
+
if (columnNumber !== undefined) {
|
|
40
|
+
logpoint.columnNumber = Number(columnNumber);
|
|
41
|
+
}
|
|
42
|
+
if (logpoint.lineNumber < 1 || logpoint.columnNumber === 0) {
|
|
43
|
+
throw new Error(`Invalid logpoint "${text}": line and column numbers are 1-based.`);
|
|
44
|
+
}
|
|
45
|
+
return logpoint;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Checks that the expression parses as `console.log()` arguments. V8 is used
|
|
49
|
+
* for parsing both here and in the browser, so an expression that is rejected
|
|
50
|
+
* here would also be rejected as a breakpoint condition. The script is only
|
|
51
|
+
* compiled, never run.
|
|
52
|
+
*/
|
|
53
|
+
export function validateExpression(expression) {
|
|
54
|
+
try {
|
|
55
|
+
new Script(`console.log(${expression})`);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
throw new Error(`The logpoint expression \`${expression}\` does not compile: ${error instanceof Error ? error.message : String(error)}. Provide the expression like arguments to console.log(...).`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function toUserCondition(expression) {
|
|
62
|
+
return expression;
|
|
63
|
+
}
|
|
64
|
+
function isFromSourceMap(uiSourceCode) {
|
|
65
|
+
return uiSourceCode.contentType().isFromSourceMap();
|
|
66
|
+
}
|
|
67
|
+
function locationKey(url, lineNumber, columnNumber) {
|
|
68
|
+
return `${url}:${lineNumber}${columnNumber === undefined ? '' : `:${columnNumber}`}`;
|
|
69
|
+
}
|
|
70
|
+
function baseName(url) {
|
|
71
|
+
return url.slice(url.lastIndexOf('/') + 1).toLowerCase();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* How long to wait for the source maps of the page to load before an original
|
|
75
|
+
* source URL is reported as unknown. Loads still pending then continue in the
|
|
76
|
+
* background, so a retry finds the source.
|
|
77
|
+
*/
|
|
78
|
+
const SOURCE_MAP_LOAD_TIMEOUT_MS = 5_000;
|
|
79
|
+
/**
|
|
80
|
+
* Manages logpoints for a single page on top of the `BreakpointManager` of
|
|
81
|
+
* the page's DevTools universe.
|
|
82
|
+
*
|
|
83
|
+
* A logpoint is a DevTools breakpoint with `isLogpoint` set: its condition
|
|
84
|
+
* logs the expression and never pauses. The breakpoint manager resolves the
|
|
85
|
+
* location through source maps, substitutes minified variable names in the
|
|
86
|
+
* expression and keeps the breakpoint bound across reloads and rebuilt
|
|
87
|
+
* bundles. This class only maps the tool API (URLs and 1-based positions)
|
|
88
|
+
* onto workspace source codes.
|
|
89
|
+
*/
|
|
90
|
+
export class LogpointManager {
|
|
91
|
+
#universe;
|
|
92
|
+
#target;
|
|
93
|
+
/**
|
|
94
|
+
* The breakpoints that are logpoints of this page. The breakpoint manager
|
|
95
|
+
* keeps one `Breakpoint` per normalized location, so two requested
|
|
96
|
+
* locations can share one entry.
|
|
97
|
+
*/
|
|
98
|
+
#records = new Set();
|
|
99
|
+
constructor(devTools) {
|
|
100
|
+
this.#universe = devTools.universe;
|
|
101
|
+
this.#target = devTools.target;
|
|
102
|
+
}
|
|
103
|
+
get #debuggerModel() {
|
|
104
|
+
const model = this.#target.model(DevTools.DebuggerModel);
|
|
105
|
+
if (!model) {
|
|
106
|
+
throw new Error('The DevTools debugger model is not available.');
|
|
107
|
+
}
|
|
108
|
+
return model;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Makes the given logpoints the complete set of logpoints on the page:
|
|
112
|
+
* logpoints not in the list are removed, new ones are added and the
|
|
113
|
+
* expression of an existing logpoint at the same location is replaced.
|
|
114
|
+
* Expressions and locations are validated before anything is changed.
|
|
115
|
+
* Throws a {@link LogpointError} when a location has no executable code;
|
|
116
|
+
* the other logpoints are applied regardless.
|
|
117
|
+
*/
|
|
118
|
+
async setLogpoints(desired) {
|
|
119
|
+
for (const options of desired) {
|
|
120
|
+
validateExpression(options.expression);
|
|
121
|
+
}
|
|
122
|
+
const resolved = new Map();
|
|
123
|
+
for (const options of desired) {
|
|
124
|
+
const uiSourceCode = await this.#findUISourceCode(options.url);
|
|
125
|
+
const key = locationKey(uiSourceCode.url(), options.lineNumber, options.columnNumber);
|
|
126
|
+
// The last entry for a location wins, like setting it twice would.
|
|
127
|
+
resolved.set(key, { options, uiSourceCode, key });
|
|
128
|
+
}
|
|
129
|
+
const kept = new Set();
|
|
130
|
+
const unbound = [];
|
|
131
|
+
for (const { options, uiSourceCode, key } of resolved.values()) {
|
|
132
|
+
const breakpoint = await this.#universe.breakpointManager.setBreakpoint(uiSourceCode,
|
|
133
|
+
// The tool API is 1-based like the DevTools UI; the model is 0-based.
|
|
134
|
+
options.lineNumber - 1, options.columnNumber === undefined
|
|
135
|
+
? undefined
|
|
136
|
+
: options.columnNumber - 1, toUserCondition(options.expression),
|
|
137
|
+
/* enabled */ true,
|
|
138
|
+
/* isLogpoint */ true, "USER_ACTION" /* DevTools.BreakpointManager.BreakpointOrigin.USER_ACTION */);
|
|
139
|
+
if (!breakpoint) {
|
|
140
|
+
unbound.push(key);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
// Wait for the breakpoint to be set in the backend so that the result
|
|
144
|
+
// reports whether the location resolved.
|
|
145
|
+
await breakpoint.updateBreakpoint();
|
|
146
|
+
if (!this.#records.has(breakpoint) && !breakpoint.bound()) {
|
|
147
|
+
await breakpoint.remove(/* keepInStorage */ false);
|
|
148
|
+
unbound.push(key);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
this.#records.add(breakpoint);
|
|
152
|
+
kept.add(breakpoint);
|
|
153
|
+
}
|
|
154
|
+
for (const breakpoint of [...this.#records]) {
|
|
155
|
+
if (!kept.has(breakpoint)) {
|
|
156
|
+
this.#records.delete(breakpoint);
|
|
157
|
+
await breakpoint.remove(/* keepInStorage */ false);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
this.#setLazySourceMapLoading(this.#records.size === 0);
|
|
161
|
+
const logpoints = this.#currentLogpoints();
|
|
162
|
+
if (unbound.length) {
|
|
163
|
+
throw new LogpointError(`No executable code at ${unbound.join(', ')} (for example a comment, type annotation or empty line, or a line not mapped to the generated script). Pick lines containing executable code. The other logpoints were applied.`, logpoints);
|
|
164
|
+
}
|
|
165
|
+
return logpoints;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Releases the records. The breakpoints are not removed from the backend:
|
|
169
|
+
* the universe is being disposed and its session detaches, which removes
|
|
170
|
+
* them along with the page.
|
|
171
|
+
*/
|
|
172
|
+
dispose() {
|
|
173
|
+
this.#records.clear();
|
|
174
|
+
}
|
|
175
|
+
#currentLogpoints() {
|
|
176
|
+
return [...this.#records].map(breakpoint => this.#snapshot(breakpoint));
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Reads the current state of a breakpoint, so that the result reflects
|
|
180
|
+
* the normalized location and the re-binding done by the breakpoint
|
|
181
|
+
* manager after reloads. The breakpoint manager may have moved a
|
|
182
|
+
* generated location to its original source, so whether the logpoint is
|
|
183
|
+
* source-mapped follows from the breakpoint, not from the request.
|
|
184
|
+
*/
|
|
185
|
+
#snapshot(breakpoint) {
|
|
186
|
+
const sourceMapped = [...breakpoint.getUiSourceCodes()].some(isFromSourceMap);
|
|
187
|
+
const modelBreakpoint = breakpoint.modelBreakpoint(this.#debuggerModel);
|
|
188
|
+
const generatedLocations = [];
|
|
189
|
+
if (breakpoint.bound()) {
|
|
190
|
+
for (const position of modelBreakpoint?.currentState ?? []) {
|
|
191
|
+
generatedLocations.push({
|
|
192
|
+
url: position.url,
|
|
193
|
+
lineNumber: position.lineNumber + 1,
|
|
194
|
+
columnNumber: (position.columnNumber ?? 0) + 1,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const columnNumber = breakpoint.columnNumber();
|
|
199
|
+
return {
|
|
200
|
+
url: breakpoint.url(),
|
|
201
|
+
lineNumber: breakpoint.lineNumber() + 1,
|
|
202
|
+
columnNumber: columnNumber === undefined ? undefined : columnNumber + 1,
|
|
203
|
+
expression: breakpoint.condition(),
|
|
204
|
+
sourceMapped,
|
|
205
|
+
generatedLocations,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Source maps load lazily by default, so after a reload the original
|
|
210
|
+
* sources of a rebuilt bundle would not re-appear in the workspace and a
|
|
211
|
+
* logpoint on an original source file could not be re-bound to the new
|
|
212
|
+
* generated location. While logpoints exist, source maps are loaded
|
|
213
|
+
* eagerly instead, like DevTools does with the Sources panel open.
|
|
214
|
+
*/
|
|
215
|
+
#setLazySourceMapLoading(lazy) {
|
|
216
|
+
this.#universe.settings
|
|
217
|
+
.resolve(DevTools.SourceMapManager.lazyLoadingSettingDescriptor)
|
|
218
|
+
.set(lazy);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Finds the workspace source code for a script or original source URL,
|
|
222
|
+
* loading source maps if needed. Throws a descriptive error, including
|
|
223
|
+
* sources with the same file name, when nothing matches.
|
|
224
|
+
*/
|
|
225
|
+
async #findUISourceCode(url) {
|
|
226
|
+
// Source maps are loaded lazily in the DevTools universe and original
|
|
227
|
+
// sources only appear in the workspace once their source map has loaded.
|
|
228
|
+
let match = this.#matchUISourceCode(url);
|
|
229
|
+
let timedOut = false;
|
|
230
|
+
if (match && !isFromSourceMap(match)) {
|
|
231
|
+
// The script may be a transpiled file served at the URL of its original
|
|
232
|
+
// source (see #matchUISourceCode); its source map decides.
|
|
233
|
+
const matchedUrl = match.url();
|
|
234
|
+
const scripts = this.#debuggerModel
|
|
235
|
+
.scripts()
|
|
236
|
+
.filter(script => script.sourceURL === matchedUrl);
|
|
237
|
+
if (scripts.some(script => script.sourceMapURL)) {
|
|
238
|
+
const signal = AbortSignal.timeout(SOURCE_MAP_LOAD_TIMEOUT_MS);
|
|
239
|
+
await waitForSourceMaps(this.#debuggerModel, scripts, signal);
|
|
240
|
+
match = this.#matchUISourceCode(url);
|
|
241
|
+
if (signal.aborted && (!match || !isFromSourceMap(match))) {
|
|
242
|
+
// Setting the logpoint on the served script would use its line
|
|
243
|
+
// numbers instead of those of the original source.
|
|
244
|
+
throw new Error(`The source map of ${matchedUrl} was still loading after ${SOURCE_MAP_LOAD_TIMEOUT_MS / 1000} seconds; retry.`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
else if (!match) {
|
|
249
|
+
const signal = AbortSignal.timeout(SOURCE_MAP_LOAD_TIMEOUT_MS);
|
|
250
|
+
await waitForSourceMaps(this.#debuggerModel, this.#debuggerModel.scripts(), signal);
|
|
251
|
+
timedOut = signal.aborted;
|
|
252
|
+
match = this.#matchUISourceCode(url);
|
|
253
|
+
}
|
|
254
|
+
if (match) {
|
|
255
|
+
return match;
|
|
256
|
+
}
|
|
257
|
+
const name = baseName(url);
|
|
258
|
+
const similar = [...this.#pageSourceCodes()]
|
|
259
|
+
.map(uiSourceCode => uiSourceCode.url())
|
|
260
|
+
.filter(candidate => baseName(candidate) === name)
|
|
261
|
+
.slice(0, 5);
|
|
262
|
+
throw new Error(`No parsed script or original source file matches "${url}". The url must match the URL of a script as served to the browser or an original source file from a source map, exactly or as a path suffix.${timedOut ? ` Some source maps were still loading after ${SOURCE_MAP_LOAD_TIMEOUT_MS / 1000} seconds; retry if the file comes from a source map.` : ''}${similar.length ? ` Sources with the same file name: ${similar.join(', ')}.` : ''}`);
|
|
263
|
+
}
|
|
264
|
+
/** The scripts and source-map sources of the page in the workspace. */
|
|
265
|
+
*#pageSourceCodes() {
|
|
266
|
+
for (const uiSourceCode of this.#universe.workspace.uiSourceCodes()) {
|
|
267
|
+
const projectType = uiSourceCode.project().type();
|
|
268
|
+
// Content scripts of extensions are left out: they are not part of the
|
|
269
|
+
// page under development and would make path-suffix matches ambiguous.
|
|
270
|
+
if (projectType === DevTools.Workspace.Workspace.projectTypes.Network) {
|
|
271
|
+
yield uiSourceCode;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Finds the workspace source code for a script or original source URL.
|
|
277
|
+
* An exact URL match wins; otherwise the URL is matched as a path suffix so
|
|
278
|
+
* that users can pass "src/app.ts" without knowing the bundler's URL
|
|
279
|
+
* scheme. An ambiguous suffix match is reported as an error.
|
|
280
|
+
*/
|
|
281
|
+
#matchUISourceCode(url) {
|
|
282
|
+
const candidates = new Map();
|
|
283
|
+
for (const uiSourceCode of this.#pageSourceCodes()) {
|
|
284
|
+
const candidateUrl = uiSourceCode.url();
|
|
285
|
+
if (candidateUrl !== url && !candidateUrl.endsWith('/' + url)) {
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
// Dev servers like Vite serve a transpiled file at the URL of its
|
|
289
|
+
// original source, with a source map pointing back at that same URL.
|
|
290
|
+
// The original source then has the line numbers of the file on disk,
|
|
291
|
+
// so it wins over the script served at the same URL.
|
|
292
|
+
const existing = candidates.get(candidateUrl);
|
|
293
|
+
if (!existing || isFromSourceMap(uiSourceCode)) {
|
|
294
|
+
candidates.set(candidateUrl, uiSourceCode);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
const exact = candidates.get(url);
|
|
298
|
+
if (exact) {
|
|
299
|
+
return exact;
|
|
300
|
+
}
|
|
301
|
+
if (candidates.size > 1) {
|
|
302
|
+
throw new Error(`"${url}" matches multiple sources: ${[...candidates.keys()].join(', ')}. Specify a longer path.`);
|
|
303
|
+
}
|
|
304
|
+
return candidates.values().next().value ?? null;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
//# sourceMappingURL=LogpointManager.js.map
|