@gabrielbryk/arc-devtools-mcp 1.3.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.
Files changed (367) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +977 -0
  3. package/build/src/HeapSnapshotManager.js +148 -0
  4. package/build/src/McpContext.js +737 -0
  5. package/build/src/McpPage.js +315 -0
  6. package/build/src/McpResponse.js +990 -0
  7. package/build/src/Mutex.js +38 -0
  8. package/build/src/PageCollector.js +297 -0
  9. package/build/src/ServiceWorkerCollector.js +171 -0
  10. package/build/src/SlimMcpResponse.js +19 -0
  11. package/build/src/TextSnapshot.js +236 -0
  12. package/build/src/ToolHandler.js +223 -0
  13. package/build/src/WaitForHelper.js +190 -0
  14. package/build/src/bin/check-latest-version.js +50 -0
  15. package/build/src/bin/chrome-devtools-cli-options.js +978 -0
  16. package/build/src/bin/chrome-devtools-mcp-cli-options.js +412 -0
  17. package/build/src/bin/chrome-devtools-mcp-main.js +72 -0
  18. package/build/src/bin/chrome-devtools-mcp.js +23 -0
  19. package/build/src/bin/chrome-devtools.js +189 -0
  20. package/build/src/browser.js +253 -0
  21. package/build/src/daemon/client.js +160 -0
  22. package/build/src/daemon/daemon.js +261 -0
  23. package/build/src/daemon/types.js +7 -0
  24. package/build/src/daemon/utils.js +115 -0
  25. package/build/src/devtools/DevToolsConnectionAdapter.js +70 -0
  26. package/build/src/devtools/DevtoolsUtils.js +369 -0
  27. package/build/src/devtools/McpHostBindingAdapter.js +165 -0
  28. package/build/src/formatters/ConsoleFormatter.js +288 -0
  29. package/build/src/formatters/HeapSnapshotFormatter.js +97 -0
  30. package/build/src/formatters/IssueFormatter.js +193 -0
  31. package/build/src/formatters/NetworkFormatter.js +238 -0
  32. package/build/src/formatters/SnapshotFormatter.js +135 -0
  33. package/build/src/index.js +153 -0
  34. package/build/src/issue-descriptions.js +40 -0
  35. package/build/src/logger.js +37 -0
  36. package/build/src/polyfill.js +8 -0
  37. package/build/src/telemetry/ClearcutLogger.js +169 -0
  38. package/build/src/telemetry/WatchdogClient.js +61 -0
  39. package/build/src/telemetry/errors.js +18 -0
  40. package/build/src/telemetry/flagUtils.js +89 -0
  41. package/build/src/telemetry/metricsRegistry.js +89 -0
  42. package/build/src/telemetry/persistence.js +72 -0
  43. package/build/src/telemetry/transformation.js +134 -0
  44. package/build/src/telemetry/types.js +31 -0
  45. package/build/src/telemetry/watchdog/ClearcutSender.js +205 -0
  46. package/build/src/telemetry/watchdog/main.js +128 -0
  47. package/build/src/third_party/THIRD_PARTY_NOTICES +3637 -0
  48. package/build/src/third_party/bundled-packages.json +12 -0
  49. package/build/src/third_party/devtools-formatter-worker.js +15301 -0
  50. package/build/src/third_party/devtools-heap-snapshot-worker.js +9870 -0
  51. package/build/src/third_party/index.js +159597 -0
  52. package/build/src/third_party/issue-descriptions/CoepCoopSandboxedIframeCannotNavigateToCoopPage.md +4 -0
  53. package/build/src/third_party/issue-descriptions/CoepCorpNotSameOrigin.md +8 -0
  54. package/build/src/third_party/issue-descriptions/CoepCorpNotSameOriginAfterDefaultedToSameOriginByCoep.md +18 -0
  55. package/build/src/third_party/issue-descriptions/CoepCorpNotSameSite.md +7 -0
  56. package/build/src/third_party/issue-descriptions/CoepFrameResourceNeedsCoepHeader.md +10 -0
  57. package/build/src/third_party/issue-descriptions/CompatibilityModeQuirks.md +5 -0
  58. package/build/src/third_party/issue-descriptions/CookieAttributeValueExceedsMaxSize.md +5 -0
  59. package/build/src/third_party/issue-descriptions/LowTextContrast.md +5 -0
  60. package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeRead.md +8 -0
  61. package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeSet.md +8 -0
  62. package/build/src/third_party/issue-descriptions/SameSiteExcludeNavigationContextDowngrade.md +8 -0
  63. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorRead.md +8 -0
  64. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorSet.md +8 -0
  65. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnRead.md +8 -0
  66. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnSet.md +8 -0
  67. package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeRead.md +9 -0
  68. package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeSet.md +9 -0
  69. package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeRead.md +8 -0
  70. package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeSet.md +8 -0
  71. package/build/src/third_party/issue-descriptions/SameSiteWarnStrictLaxDowngradeStrict.md +8 -0
  72. package/build/src/third_party/issue-descriptions/arInsecureContext.md +7 -0
  73. package/build/src/third_party/issue-descriptions/arInvalidInfoHeader.md +5 -0
  74. package/build/src/third_party/issue-descriptions/arInvalidRegisterOsSourceHeader.md +5 -0
  75. package/build/src/third_party/issue-descriptions/arInvalidRegisterOsTriggerHeader.md +5 -0
  76. package/build/src/third_party/issue-descriptions/arInvalidRegisterSourceHeader.md +5 -0
  77. package/build/src/third_party/issue-descriptions/arInvalidRegisterTriggerHeader.md +5 -0
  78. package/build/src/third_party/issue-descriptions/arNavigationRegistrationUniqueScopeAlreadySet.md +5 -0
  79. package/build/src/third_party/issue-descriptions/arNavigationRegistrationWithoutTransientUserActivation.md +6 -0
  80. package/build/src/third_party/issue-descriptions/arNoRegisterOsSourceHeader.md +5 -0
  81. package/build/src/third_party/issue-descriptions/arNoRegisterOsTriggerHeader.md +5 -0
  82. package/build/src/third_party/issue-descriptions/arNoRegisterSourceHeader.md +5 -0
  83. package/build/src/third_party/issue-descriptions/arNoRegisterTriggerHeader.md +5 -0
  84. package/build/src/third_party/issue-descriptions/arNoWebOrOsSupport.md +4 -0
  85. package/build/src/third_party/issue-descriptions/arOsSourceIgnored.md +18 -0
  86. package/build/src/third_party/issue-descriptions/arOsTriggerIgnored.md +19 -0
  87. package/build/src/third_party/issue-descriptions/arPermissionPolicyDisabled.md +8 -0
  88. package/build/src/third_party/issue-descriptions/arSourceAndTriggerHeaders.md +9 -0
  89. package/build/src/third_party/issue-descriptions/arSourceIgnored.md +13 -0
  90. package/build/src/third_party/issue-descriptions/arTriggerIgnored.md +12 -0
  91. package/build/src/third_party/issue-descriptions/arUntrustworthyReportingOrigin.md +10 -0
  92. package/build/src/third_party/issue-descriptions/arWebAndOsHeaders.md +11 -0
  93. package/build/src/third_party/issue-descriptions/bounceTrackingMitigations.md +3 -0
  94. package/build/src/third_party/issue-descriptions/clientHintMetaTagAllowListInvalidOrigin.md +4 -0
  95. package/build/src/third_party/issue-descriptions/clientHintMetaTagModifiedHTML.md +4 -0
  96. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowlistItemType.md +12 -0
  97. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidHeader.md +12 -0
  98. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidUrlPattern.md +8 -0
  99. package/build/src/third_party/issue-descriptions/connectionAllowlistItemNotInnerList.md +12 -0
  100. package/build/src/third_party/issue-descriptions/connectionAllowlistMoreThanOneList.md +7 -0
  101. package/build/src/third_party/issue-descriptions/connectionAllowlistReportingEndpointNotToken.md +10 -0
  102. package/build/src/third_party/issue-descriptions/cookieCrossSiteRedirectDowngrade.md +12 -0
  103. package/build/src/third_party/issue-descriptions/cookieExcludeBlockedWithinRelatedWebsiteSet.md +4 -0
  104. package/build/src/third_party/issue-descriptions/cookieExcludeDomainNonAscii.md +11 -0
  105. package/build/src/third_party/issue-descriptions/cookieExcludePortMismatch.md +8 -0
  106. package/build/src/third_party/issue-descriptions/cookieExcludeSchemeMismatch.md +7 -0
  107. package/build/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutRead.md +6 -0
  108. package/build/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutSet.md +6 -0
  109. package/build/src/third_party/issue-descriptions/cookieWarnDomainNonAscii.md +11 -0
  110. package/build/src/third_party/issue-descriptions/cookieWarnMetadataGrantRead.md +4 -0
  111. package/build/src/third_party/issue-descriptions/cookieWarnMetadataGrantSet.md +4 -0
  112. package/build/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutRead.md +6 -0
  113. package/build/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutSet.md +6 -0
  114. package/build/src/third_party/issue-descriptions/corsAllowCredentialsRequired.md +6 -0
  115. package/build/src/third_party/issue-descriptions/corsDisabledScheme.md +7 -0
  116. package/build/src/third_party/issue-descriptions/corsDisallowedByMode.md +7 -0
  117. package/build/src/third_party/issue-descriptions/corsHeaderDisallowedByPreflightResponse.md +5 -0
  118. package/build/src/third_party/issue-descriptions/corsInvalidHeaderValues.md +7 -0
  119. package/build/src/third_party/issue-descriptions/corsLocalNetworkAccessPermissionDenied.md +19 -0
  120. package/build/src/third_party/issue-descriptions/corsMethodDisallowedByPreflightResponse.md +5 -0
  121. package/build/src/third_party/issue-descriptions/corsNoCorsRedirectModeNotFollow.md +5 -0
  122. package/build/src/third_party/issue-descriptions/corsOriginMismatch.md +6 -0
  123. package/build/src/third_party/issue-descriptions/corsPreflightResponseInvalid.md +5 -0
  124. package/build/src/third_party/issue-descriptions/corsRedirectContainsCredentials.md +5 -0
  125. package/build/src/third_party/issue-descriptions/corsWildcardOriginNotAllowed.md +8 -0
  126. package/build/src/third_party/issue-descriptions/cspEvalViolation.md +9 -0
  127. package/build/src/third_party/issue-descriptions/cspInlineViolation.md +10 -0
  128. package/build/src/third_party/issue-descriptions/cspTrustedTypesPolicyViolation.md +5 -0
  129. package/build/src/third_party/issue-descriptions/cspTrustedTypesSinkViolation.md +8 -0
  130. package/build/src/third_party/issue-descriptions/cspURLViolation.md +10 -0
  131. package/build/src/third_party/issue-descriptions/deprecation.md +3 -0
  132. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsEmptyList.md +1 -0
  133. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsHttpNotFound.md +1 -0
  134. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidContentType.md +1 -0
  135. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidResponse.md +1 -0
  136. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsNoResponse.md +1 -0
  137. package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsFetchFailed.md +1 -0
  138. package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsInvalidRecord.md +1 -0
  139. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownHttpNotFound.md +1 -0
  140. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidContentType.md +1 -0
  141. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidResponse.md +1 -0
  142. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownNoResponse.md +1 -0
  143. package/build/src/third_party/issue-descriptions/emailVerificationRequestInvalidEmail.md +1 -0
  144. package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksHttpNotFound.md +1 -0
  145. package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksInvalidResponse.md +1 -0
  146. package/build/src/third_party/issue-descriptions/emailVerificationRequestKeyBindingSigningFailed.md +1 -0
  147. package/build/src/third_party/issue-descriptions/emailVerificationRequestRpOriginIsOpaque.md +1 -0
  148. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenHttpNotFound.md +1 -0
  149. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidContentType.md +1 -0
  150. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidResponse.md +1 -0
  151. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidSdJwt.md +1 -0
  152. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenMalformedSdJwt.md +1 -0
  153. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenNoResponse.md +1 -0
  154. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidAudience.md +1 -0
  155. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidIssuedAt.md +1 -0
  156. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidNonce.md +1 -0
  157. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidSdHash.md +1 -0
  158. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidTyp.md +1 -0
  159. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingAud.md +1 -0
  160. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingCnf.md +1 -0
  161. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingIat.md +1 -0
  162. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingNonce.md +1 -0
  163. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingSdHash.md +1 -0
  164. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbSignatureFailed.md +1 -0
  165. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmail.md +1 -0
  166. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmailVerified.md +1 -0
  167. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidHolderKey.md +1 -0
  168. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuedAt.md +1 -0
  169. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuer.md +1 -0
  170. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtJwksMissingKeys.md +1 -0
  171. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingCnf.md +1 -0
  172. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingEmail.md +1 -0
  173. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIat.md +1 -0
  174. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIss.md +1 -0
  175. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtSignatureFailed.md +1 -0
  176. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtUnsupportedHeaderAlg.md +1 -0
  177. package/build/src/third_party/issue-descriptions/emailVerificationRequestUserLoggedOut.md +1 -0
  178. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownAccountsEndpointCrossOrigin.md +1 -0
  179. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownHttpNotFound.md +1 -0
  180. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidContentType.md +1 -0
  181. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidResponse.md +1 -0
  182. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownIssuanceEndpointCrossOrigin.md +1 -0
  183. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownListEmpty.md +1 -0
  184. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingAccountsEndpoint.md +1 -0
  185. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingIssuanceEndpoint.md +1 -0
  186. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownNoResponse.md +1 -0
  187. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownUnsupportedSigningAlgorithm.md +1 -0
  188. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsHttpNotFound.md +1 -0
  189. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsInvalidResponse.md +1 -0
  190. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsNoResponse.md +1 -0
  191. package/build/src/third_party/issue-descriptions/federatedAuthRequestApprovalDeclined.md +1 -0
  192. package/build/src/third_party/issue-descriptions/federatedAuthRequestCanceled.md +1 -0
  193. package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorFetchingSignin.md +1 -0
  194. package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorIdToken.md +1 -0
  195. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenHttpNotFound.md +1 -0
  196. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidRequest.md +1 -0
  197. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidResponse.md +1 -0
  198. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenNoResponse.md +1 -0
  199. package/build/src/third_party/issue-descriptions/federatedAuthRequestInvalidSigninResponse.md +1 -0
  200. package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestHttpNotFound.md +1 -0
  201. package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestInvalidResponse.md +1 -0
  202. package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestNoResponse.md +1 -0
  203. package/build/src/third_party/issue-descriptions/federatedAuthRequestTooManyRequests.md +1 -0
  204. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidAccountsResponse.md +1 -0
  205. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidConfigOrWellKnown.md +1 -0
  206. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoAccountSharingPermission.md +1 -0
  207. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoApiPermission.md +1 -0
  208. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoReturningUserFromFetchedAccounts.md +1 -0
  209. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotIframe.md +1 -0
  210. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotPotentiallyTrustworthy.md +1 -0
  211. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSameOrigin.md +1 -0
  212. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSignedInWithIdp.md +1 -0
  213. package/build/src/third_party/issue-descriptions/fetchingPartitionedBlobURL.md +7 -0
  214. package/build/src/third_party/issue-descriptions/genericBackUINavigationWouldSkipAd.md +4 -0
  215. package/build/src/third_party/issue-descriptions/genericFormAriaLabelledByToNonExistingIdError.md +8 -0
  216. package/build/src/third_party/issue-descriptions/genericFormAutocompleteAttributeEmptyError.md +5 -0
  217. package/build/src/third_party/issue-descriptions/genericFormDuplicateIdForInputError.md +5 -0
  218. package/build/src/third_party/issue-descriptions/genericFormEmptyIdAndNameAttributesForInputError.md +5 -0
  219. package/build/src/third_party/issue-descriptions/genericFormInputAssignedAutocompleteValueToIdOrNameAttributeError.md +5 -0
  220. package/build/src/third_party/issue-descriptions/genericFormInputHasWrongButWellIntendedAutocompleteValueError.md +5 -0
  221. package/build/src/third_party/issue-descriptions/genericFormInputWithNoLabelError.md +5 -0
  222. package/build/src/third_party/issue-descriptions/genericFormLabelForMatchesNonExistingIdError.md +5 -0
  223. package/build/src/third_party/issue-descriptions/genericFormLabelForNameError.md +5 -0
  224. package/build/src/third_party/issue-descriptions/genericFormLabelHasNeitherForNorNestedInputError.md +5 -0
  225. package/build/src/third_party/issue-descriptions/genericFormModelContextMissingToolDescription.md +5 -0
  226. package/build/src/third_party/issue-descriptions/genericFormModelContextMissingToolName.md +5 -0
  227. package/build/src/third_party/issue-descriptions/genericFormModelContextParameterMissingName.md +5 -0
  228. package/build/src/third_party/issue-descriptions/genericFormModelContextParameterMissingTitleAndDescription.md +5 -0
  229. package/build/src/third_party/issue-descriptions/genericFormModelContextRequiredParameterMissingName.md +5 -0
  230. package/build/src/third_party/issue-descriptions/genericNavigationEntryMarkedSkippable.md +7 -0
  231. package/build/src/third_party/issue-descriptions/genericResponseWasBlockedByORB.md +4 -0
  232. package/build/src/third_party/issue-descriptions/heavyAd.md +10 -0
  233. package/build/src/third_party/issue-descriptions/mixedContent.md +5 -0
  234. package/build/src/third_party/issue-descriptions/navigatingPartitionedBlobURL.md +5 -0
  235. package/build/src/third_party/issue-descriptions/permissionElementActivationDisabled.md +7 -0
  236. package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluder.md +9 -0
  237. package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluderParent.md +9 -0
  238. package/build/src/third_party/issue-descriptions/permissionElementCspFrameAncestorsMissing.md +5 -0
  239. package/build/src/third_party/issue-descriptions/permissionElementFencedFrameDisallowed.md +5 -0
  240. package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooLarge.md +5 -0
  241. package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooSmall.md +5 -0
  242. package/build/src/third_party/issue-descriptions/permissionElementGeolocationDeprecated.md +5 -0
  243. package/build/src/third_party/issue-descriptions/permissionElementInsetBoxShadowUnsupported.md +5 -0
  244. package/build/src/third_party/issue-descriptions/permissionElementInvalidDisplayStyle.md +5 -0
  245. package/build/src/third_party/issue-descriptions/permissionElementInvalidSizeValue.md +5 -0
  246. package/build/src/third_party/issue-descriptions/permissionElementInvalidType.md +5 -0
  247. package/build/src/third_party/issue-descriptions/permissionElementInvalidTypeActivation.md +5 -0
  248. package/build/src/third_party/issue-descriptions/permissionElementLowContrast.md +5 -0
  249. package/build/src/third_party/issue-descriptions/permissionElementNonOpaqueColor.md +5 -0
  250. package/build/src/third_party/issue-descriptions/permissionElementPaddingBottomUnsupported.md +6 -0
  251. package/build/src/third_party/issue-descriptions/permissionElementPaddingRightUnsupported.md +6 -0
  252. package/build/src/third_party/issue-descriptions/permissionElementPermissionsPolicyBlocked.md +5 -0
  253. package/build/src/third_party/issue-descriptions/permissionElementRegistrationFailed.md +5 -0
  254. package/build/src/third_party/issue-descriptions/permissionElementRequestInProgress.md +5 -0
  255. package/build/src/third_party/issue-descriptions/permissionElementSecurityChecksFailed.md +5 -0
  256. package/build/src/third_party/issue-descriptions/permissionElementTypeNotSupported.md +5 -0
  257. package/build/src/third_party/issue-descriptions/permissionElementUntrustedEvent.md +7 -0
  258. package/build/src/third_party/issue-descriptions/placeholderDescriptionForInvisibleIssues.md +3 -0
  259. package/build/src/third_party/issue-descriptions/propertyRuleInvalidNameIssue.md +3 -0
  260. package/build/src/third_party/issue-descriptions/propertyRuleIssue.md +7 -0
  261. package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedOptGroupChild.md +7 -0
  262. package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedSelectChild.md +7 -0
  263. package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentAttributesSelectDescendant.md +3 -0
  264. package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentLegendChild.md +3 -0
  265. package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentOptionChild.md +3 -0
  266. package/build/src/third_party/issue-descriptions/selectElementAccessibilityNonPhrasingContentOptionChild.md +3 -0
  267. package/build/src/third_party/issue-descriptions/selectivePermissionsIntervention.md +7 -0
  268. package/build/src/third_party/issue-descriptions/sharedArrayBuffer.md +7 -0
  269. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorCrossOriginNoCorsRequest.md +1 -0
  270. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorDictionaryLoadFailure.md +3 -0
  271. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorMatchingDictionaryNotUsed.md +3 -0
  272. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorUnexpectedContentDictionaryHeader.md +1 -0
  273. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorCossOriginNoCorsRequest.md +1 -0
  274. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorDisallowedBySettings.md +1 -0
  275. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorExpiredResponse.md +3 -0
  276. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorFeatureDisabled.md +3 -0
  277. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInsufficientResources.md +1 -0
  278. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidMatchField.md +1 -0
  279. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidStructuredHeader.md +1 -0
  280. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidTTLField.md +1 -0
  281. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNavigationRequest.md +3 -0
  282. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNoMatchField.md +1 -0
  283. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonIntegerTTLField.md +1 -0
  284. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonListMatchDestField.md +1 -0
  285. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonSecureContext.md +3 -0
  286. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringIdField.md +1 -0
  287. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringInMatchDestList.md +1 -0
  288. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringMatchField.md +1 -0
  289. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonTokenTypeField.md +1 -0
  290. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorRequestAborted.md +1 -0
  291. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorShuttingDown.md +1 -0
  292. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorTooLongIdField.md +3 -0
  293. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorUnsupportedType.md +3 -0
  294. package/build/src/third_party/issue-descriptions/sriInvalidSignatureHeader.md +14 -0
  295. package/build/src/third_party/issue-descriptions/sriInvalidSignatureInputHeader.md +15 -0
  296. package/build/src/third_party/issue-descriptions/sriMissingSignatureHeader.md +8 -0
  297. package/build/src/third_party/issue-descriptions/sriMissingSignatureInputHeader.md +7 -0
  298. package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsIncorrectLength.md +11 -0
  299. package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsNotByteSequence.md +14 -0
  300. package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsParameterized.md +15 -0
  301. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentName.md +8 -0
  302. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentType.md +13 -0
  303. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidDerivedComponentParameter.md +4 -0
  304. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidHeaderComponentParameter.md +5 -0
  305. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidParameter.md +11 -0
  306. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderKeyIdLength.md +12 -0
  307. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingLabel.md +6 -0
  308. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingRequiredParameters.md +8 -0
  309. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueMissingComponents.md +11 -0
  310. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueNotInnerList.md +11 -0
  311. package/build/src/third_party/issue-descriptions/sriValidationFailedIntegrityMismatch.md +10 -0
  312. package/build/src/third_party/issue-descriptions/sriValidationFailedInvalidLength.md +5 -0
  313. package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureExpired.md +6 -0
  314. package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureMismatch.md +11 -0
  315. package/build/src/third_party/issue-descriptions/stylesheetLateImport.md +4 -0
  316. package/build/src/third_party/issue-descriptions/stylesheetRequestFailed.md +3 -0
  317. package/build/src/third_party/issue-descriptions/summaryElementAccessibilityInteractiveContentSummaryDescendant.md +3 -0
  318. package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestLength.md +12 -0
  319. package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestType.md +17 -0
  320. package/build/src/third_party/issue-descriptions/unencodedDigestMalformedDictionary.md +14 -0
  321. package/build/src/third_party/issue-descriptions/unencodedDigestUnknownAlgorithm.md +15 -0
  322. package/build/src/third_party/lighthouse-devtools-mcp-bundle.js +61598 -0
  323. package/build/src/tools/ToolDefinition.js +73 -0
  324. package/build/src/tools/categories.js +36 -0
  325. package/build/src/tools/console.js +98 -0
  326. package/build/src/tools/emulation.js +84 -0
  327. package/build/src/tools/extensions.js +101 -0
  328. package/build/src/tools/input.js +469 -0
  329. package/build/src/tools/lighthouse.js +136 -0
  330. package/build/src/tools/memory.js +227 -0
  331. package/build/src/tools/network.js +125 -0
  332. package/build/src/tools/pages.js +419 -0
  333. package/build/src/tools/performance.js +200 -0
  334. package/build/src/tools/screencast.js +117 -0
  335. package/build/src/tools/screenshot.js +169 -0
  336. package/build/src/tools/script.js +151 -0
  337. package/build/src/tools/slim/tools.js +88 -0
  338. package/build/src/tools/snapshot.js +61 -0
  339. package/build/src/tools/thirdPartyDeveloper.js +85 -0
  340. package/build/src/tools/tools.js +56 -0
  341. package/build/src/tools/webmcp.js +66 -0
  342. package/build/src/trace-processing/parse.js +85 -0
  343. package/build/src/types.js +7 -0
  344. package/build/src/utils/check-for-updates.js +74 -0
  345. package/build/src/utils/files.js +61 -0
  346. package/build/src/utils/id.js +16 -0
  347. package/build/src/utils/keyboard.js +297 -0
  348. package/build/src/utils/pagination.js +50 -0
  349. package/build/src/utils/string.js +37 -0
  350. package/build/src/utils/types.js +7 -0
  351. package/build/src/version.js +10 -0
  352. package/package.json +100 -0
  353. package/skills/a11y-debugging/SKILL.md +89 -0
  354. package/skills/a11y-debugging/references/a11y-snippets.md +92 -0
  355. package/skills/chrome-devtools/SKILL.md +72 -0
  356. package/skills/chrome-devtools-cli/SKILL.md +153 -0
  357. package/skills/chrome-devtools-cli/references/installation.md +14 -0
  358. package/skills/debug-optimize-lcp/SKILL.md +121 -0
  359. package/skills/debug-optimize-lcp/references/elements-and-size.md +27 -0
  360. package/skills/debug-optimize-lcp/references/lcp-breakdown.md +23 -0
  361. package/skills/debug-optimize-lcp/references/lcp-snippets.md +79 -0
  362. package/skills/debug-optimize-lcp/references/optimization-strategies.md +38 -0
  363. package/skills/memory-leak-debugging/SKILL.md +50 -0
  364. package/skills/memory-leak-debugging/references/common-leaks.md +33 -0
  365. package/skills/memory-leak-debugging/references/compare_snapshots.js +109 -0
  366. package/skills/memory-leak-debugging/references/memlab.md +29 -0
  367. package/skills/troubleshooting/SKILL.md +98 -0
@@ -0,0 +1,737 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import fs from 'node:fs/promises';
7
+ import fsPromises from 'node:fs/promises';
8
+ import os from 'node:os';
9
+ import path from 'node:path';
10
+ import { fileURLToPath, pathToFileURL } from 'node:url';
11
+ import { overrideDevToolsGlobals, UniverseManager, } from './devtools/DevtoolsUtils.js';
12
+ import { HeapSnapshotManager } from './HeapSnapshotManager.js';
13
+ import { McpPage } from './McpPage.js';
14
+ import { NetworkCollector, ConsoleCollector, } from './PageCollector.js';
15
+ import { ServiceWorkerConsoleCollector } from './ServiceWorkerCollector.js';
16
+ import { Locator, PredefinedNetworkConditions, } from './third_party/index.js';
17
+ import { listPages } from './tools/pages.js';
18
+ import { CLOSE_PAGE_ERROR } from './tools/ToolDefinition.js';
19
+ import { ensureExtension, getTempFilePath, resolveCanonicalPath, } from './utils/files.js';
20
+ import { getNetworkMultiplierFromString } from './WaitForHelper.js';
21
+ const DEFAULT_TIMEOUT = 5_000;
22
+ const NAVIGATION_TIMEOUT = 10_000;
23
+ // A tab is safe to reuse (instead of opening a new one) only when it is blank,
24
+ // so reusing it never discards a page the user cares about. Covers Chrome's and
25
+ // Arc's new-tab/blank URLs. A target reporting an empty URL has not navigated.
26
+ function isReusableBlankUrl(url) {
27
+ return (url === '' ||
28
+ url === 'about:blank' ||
29
+ url === 'chrome://newtab/' ||
30
+ url.startsWith('arc://newtab'));
31
+ }
32
+ export class McpContext {
33
+ browser;
34
+ logger;
35
+ // Maps LLM-provided isolatedContext name → Puppeteer BrowserContext.
36
+ #isolatedContexts = new Map();
37
+ // Auto-generated name counter for when no name is provided.
38
+ #nextIsolatedContextId = 1;
39
+ #pages = [];
40
+ #extensionServiceWorkers = [];
41
+ #mcpPages = new Map();
42
+ #selectedPage;
43
+ #networkCollector;
44
+ #consoleCollector;
45
+ #devtoolsUniverseManager;
46
+ #serviceWorkerConsoleCollector;
47
+ #isRunningTrace = false;
48
+ #screenRecorderData = null;
49
+ #nextPageId = 1;
50
+ #extensionPages = new WeakMap();
51
+ #extensionServiceWorkerMap = new WeakMap();
52
+ #nextExtensionServiceWorkerId = 1;
53
+ #traceResults = [];
54
+ #locatorClass;
55
+ #options;
56
+ #heapSnapshotManager = new HeapSnapshotManager();
57
+ #roots = undefined;
58
+ constructor(browser, logger, options, locatorClass) {
59
+ overrideDevToolsGlobals({
60
+ loadResource: (url) => {
61
+ return this.loadResource(url);
62
+ },
63
+ });
64
+ this.browser = browser;
65
+ this.logger = logger;
66
+ this.#locatorClass = locatorClass;
67
+ this.#options = options;
68
+ this.#networkCollector = new NetworkCollector(this.browser);
69
+ this.#consoleCollector = new ConsoleCollector(this.browser, collect => {
70
+ return {
71
+ console: event => {
72
+ collect(event);
73
+ },
74
+ uncaughtError: event => {
75
+ collect(event);
76
+ },
77
+ devtoolsAggregatedIssue: event => {
78
+ collect(event);
79
+ },
80
+ };
81
+ });
82
+ this.#serviceWorkerConsoleCollector = new ServiceWorkerConsoleCollector(this.browser);
83
+ this.#devtoolsUniverseManager = new UniverseManager(this.browser);
84
+ }
85
+ async #init() {
86
+ const pages = await this.createPagesSnapshot();
87
+ const workers = await this.createExtensionServiceWorkersSnapshot();
88
+ await this.#networkCollector.init(pages);
89
+ await this.#consoleCollector.init(pages);
90
+ await this.#devtoolsUniverseManager.init(pages);
91
+ await this.#serviceWorkerConsoleCollector.init(workers);
92
+ }
93
+ dispose() {
94
+ this.#networkCollector.dispose();
95
+ this.#consoleCollector.dispose();
96
+ this.#devtoolsUniverseManager.dispose();
97
+ this.#serviceWorkerConsoleCollector.dispose();
98
+ for (const mcpPage of this.#mcpPages.values()) {
99
+ mcpPage.dispose();
100
+ }
101
+ this.#mcpPages.clear();
102
+ // Isolated contexts are intentionally not closed here.
103
+ // Either the entire browser will be closed or we disconnect
104
+ // without destroying browser state.
105
+ this.#isolatedContexts.clear();
106
+ }
107
+ static async from(browser, logger, opts,
108
+ /* Let tests use unbundled Locator class to avoid overly strict checks within puppeteer that fail when mixing bundled and unbundled class instances */
109
+ locatorClass = Locator) {
110
+ const context = new McpContext(browser, logger, opts, locatorClass);
111
+ await context.#init();
112
+ return context;
113
+ }
114
+ roots() {
115
+ if (this.#roots === undefined) {
116
+ return undefined;
117
+ }
118
+ return [
119
+ ...this.#roots,
120
+ {
121
+ uri: pathToFileURL(os.tmpdir()).href,
122
+ name: 'temp',
123
+ },
124
+ ];
125
+ }
126
+ setRoots(roots) {
127
+ this.#roots = roots;
128
+ }
129
+ async validatePath(filePath) {
130
+ if (filePath === undefined) {
131
+ return;
132
+ }
133
+ const roots = this.roots();
134
+ if (roots === undefined) {
135
+ return;
136
+ }
137
+ let canonicalPath;
138
+ try {
139
+ canonicalPath = await resolveCanonicalPath(filePath);
140
+ }
141
+ catch (err) {
142
+ const errMsg = err instanceof Error ? err.message : String(err);
143
+ console.error(`[MCP Context] Error resolving real path for ${filePath}: ${errMsg}`);
144
+ throw new Error(`Access denied: Cannot resolve base path for ${filePath}.`);
145
+ }
146
+ let allowed = false;
147
+ for (const root of roots) {
148
+ try {
149
+ const rootPathUri = root.uri;
150
+ const rootPath = path.resolve(fileURLToPath(rootPathUri));
151
+ const canonicalRoot = await fsPromises.realpath(rootPath);
152
+ if (canonicalPath === canonicalRoot ||
153
+ canonicalPath.startsWith(canonicalRoot + path.sep)) {
154
+ allowed = true;
155
+ break;
156
+ }
157
+ }
158
+ catch (rootErr) {
159
+ const errMsg = rootErr instanceof Error ? rootErr.message : String(rootErr);
160
+ console.warn(`[MCP Context] Could not resolve configured root ${root.uri}: ${errMsg}`);
161
+ // Skip this root if it cannot be resolved.
162
+ }
163
+ }
164
+ if (!allowed) {
165
+ throw new Error(`Access denied: path ${filePath} (canonical: ${canonicalPath}) is not within any of the configured workspace roots.`);
166
+ }
167
+ }
168
+ resolveCdpRequestId(page, cdpRequestId) {
169
+ if (!cdpRequestId) {
170
+ this.logger?.('no network request');
171
+ return;
172
+ }
173
+ const request = this.#networkCollector.find(page.pptrPage, request => {
174
+ // @ts-expect-error id is internal.
175
+ return request.id === cdpRequestId;
176
+ });
177
+ if (!request) {
178
+ this.logger?.('no network request for ' + cdpRequestId);
179
+ return;
180
+ }
181
+ return this.#networkCollector.getIdForResource(request);
182
+ }
183
+ getNetworkRequests(page, includePreservedRequests) {
184
+ return this.#networkCollector.getData(page.pptrPage, includePreservedRequests);
185
+ }
186
+ getConsoleData(page, includePreservedMessages) {
187
+ return this.#consoleCollector.getData(page.pptrPage, includePreservedMessages);
188
+ }
189
+ getDevToolsUniverse(page) {
190
+ return this.#devtoolsUniverseManager.get(page.pptrPage);
191
+ }
192
+ getConsoleMessageStableId(message) {
193
+ return this.#consoleCollector.getIdForResource(message);
194
+ }
195
+ getConsoleMessageById(page, id) {
196
+ return this.#consoleCollector.getById(page.pptrPage, id);
197
+ }
198
+ async newPage(background, isolatedContextName) {
199
+ if (this.#options.arc) {
200
+ return this.#newPageArc(isolatedContextName);
201
+ }
202
+ let page;
203
+ if (isolatedContextName !== undefined) {
204
+ let ctx = this.#isolatedContexts.get(isolatedContextName);
205
+ if (!ctx) {
206
+ ctx = await this.browser.createBrowserContext();
207
+ this.#isolatedContexts.set(isolatedContextName, ctx);
208
+ }
209
+ page = await ctx.newPage();
210
+ }
211
+ else {
212
+ page = await this.browser.newPage({ background });
213
+ }
214
+ await this.createPagesSnapshot();
215
+ this.selectPage(this.#getMcpPage(page));
216
+ this.#networkCollector.addPage(page);
217
+ this.#consoleCollector.addPage(page);
218
+ return this.#getMcpPage(page);
219
+ }
220
+ // Arc browser crashes when a tab is created programmatically via
221
+ // Target.createTarget — which both browser.newPage() and
222
+ // createBrowserContext().newPage() trigger. In Arc mode we therefore never
223
+ // create a tab: we reuse an existing blank/new tab. Crucially we only reuse a
224
+ // *blank* tab, never a loaded one, so we never navigate the user's active page
225
+ // away. If no blank tab exists we surface an actionable error instead of
226
+ // clobbering whatever tab happens to be last.
227
+ async #newPageArc(isolatedContextName) {
228
+ if (isolatedContextName !== undefined) {
229
+ throw new Error('Isolated browser contexts are not supported in Arc mode (--arc): ' +
230
+ 'creating one requires programmatic tab creation, which crashes Arc. ' +
231
+ 'Retry without isolatedContext.');
232
+ }
233
+ await this.createPagesSnapshot();
234
+ const reusable = this.#pages.find(page => isReusableBlankUrl(page.url()));
235
+ if (!reusable) {
236
+ throw new Error('Arc mode (--arc) cannot open a new tab: Arc crashes on programmatic ' +
237
+ 'tab creation. Open a blank tab in Arc (e.g. Cmd+T) and retry — ' +
238
+ 'new_page will reuse that blank tab.');
239
+ }
240
+ const mcpPage = this.#getMcpPage(reusable);
241
+ this.selectPage(mcpPage);
242
+ // addPage is idempotent and the page is normally already tracked via the
243
+ // browser 'targetcreated' listener; call it anyway to mirror the create path.
244
+ this.#networkCollector.addPage(reusable);
245
+ this.#consoleCollector.addPage(reusable);
246
+ return mcpPage;
247
+ }
248
+ async closePage(pageId) {
249
+ if (this.#pages.length === 1) {
250
+ throw new Error(CLOSE_PAGE_ERROR);
251
+ }
252
+ const page = this.getPageById(pageId);
253
+ if (page) {
254
+ page.dispose();
255
+ this.#mcpPages.delete(page.pptrPage);
256
+ }
257
+ await page.pptrPage.close({ runBeforeUnload: false });
258
+ }
259
+ getNetworkRequestById(page, reqid) {
260
+ return this.#networkCollector.getById(page.pptrPage, reqid);
261
+ }
262
+ async restoreEmulation(page) {
263
+ const currentSetting = page.emulationSettings;
264
+ await this.emulate(currentSetting, page.pptrPage);
265
+ }
266
+ async emulate(options, targetPage) {
267
+ const page = targetPage ?? this.getSelectedPptrPage();
268
+ const mcpPage = this.#getMcpPage(page);
269
+ const newSettings = { ...mcpPage.emulationSettings };
270
+ // Skip network emulation if blocklist/allowlist is configured, as it conflicts with blocking rules in Puppeteer.
271
+ if (this.#options.hasNetworkBlockOrAllowlist) {
272
+ if (options.networkConditions !== undefined) {
273
+ throw new Error('Network throttling is not supported when network blocking (allowlist/blocklist) is configured.');
274
+ }
275
+ }
276
+ else if (!options.networkConditions) {
277
+ await page.emulateNetworkConditions(null);
278
+ delete newSettings.networkConditions;
279
+ }
280
+ else if (options.networkConditions === 'Offline') {
281
+ await page.emulateNetworkConditions({
282
+ offline: true,
283
+ download: 0,
284
+ upload: 0,
285
+ latency: 0,
286
+ });
287
+ newSettings.networkConditions = 'Offline';
288
+ }
289
+ else if (options.networkConditions in PredefinedNetworkConditions) {
290
+ const networkCondition = PredefinedNetworkConditions[options.networkConditions];
291
+ await page.emulateNetworkConditions(networkCondition);
292
+ newSettings.networkConditions = options.networkConditions;
293
+ }
294
+ const secondarySession = this.getDevToolsUniverse(mcpPage)?.session;
295
+ if (!options.cpuThrottlingRate) {
296
+ await page.emulateCPUThrottling(1);
297
+ if (secondarySession) {
298
+ await secondarySession.send('Emulation.setCPUThrottlingRate', {
299
+ rate: 1,
300
+ });
301
+ }
302
+ delete newSettings.cpuThrottlingRate;
303
+ }
304
+ else {
305
+ await page.emulateCPUThrottling(options.cpuThrottlingRate);
306
+ if (secondarySession) {
307
+ await secondarySession.send('Emulation.setCPUThrottlingRate', {
308
+ rate: options.cpuThrottlingRate,
309
+ });
310
+ }
311
+ newSettings.cpuThrottlingRate = options.cpuThrottlingRate;
312
+ }
313
+ if (!options.geolocation) {
314
+ await page.setGeolocation({ latitude: 0, longitude: 0 });
315
+ delete newSettings.geolocation;
316
+ }
317
+ else {
318
+ await page.setGeolocation(options.geolocation);
319
+ newSettings.geolocation = options.geolocation;
320
+ }
321
+ if (!options.userAgent) {
322
+ await page.setUserAgent({ userAgent: undefined });
323
+ delete newSettings.userAgent;
324
+ }
325
+ else {
326
+ await page.setUserAgent({ userAgent: options.userAgent });
327
+ newSettings.userAgent = options.userAgent;
328
+ }
329
+ if (!options.colorScheme || options.colorScheme === 'auto') {
330
+ await page.emulateMediaFeatures([
331
+ { name: 'prefers-color-scheme', value: '' },
332
+ ]);
333
+ delete newSettings.colorScheme;
334
+ }
335
+ else {
336
+ await page.emulateMediaFeatures([
337
+ { name: 'prefers-color-scheme', value: options.colorScheme },
338
+ ]);
339
+ newSettings.colorScheme = options.colorScheme;
340
+ }
341
+ if (!options.viewport) {
342
+ delete newSettings.viewport;
343
+ }
344
+ else {
345
+ const defaults = {
346
+ deviceScaleFactor: 1,
347
+ isMobile: false,
348
+ hasTouch: false,
349
+ isLandscape: false,
350
+ };
351
+ newSettings.viewport = { ...defaults, ...options.viewport };
352
+ }
353
+ if (options.extraHttpHeaders !== undefined) {
354
+ await page.setExtraHTTPHeaders(options.extraHttpHeaders);
355
+ newSettings.extraHttpHeaders = options.extraHttpHeaders;
356
+ if (Object.keys(options.extraHttpHeaders).length === 0) {
357
+ delete newSettings.extraHttpHeaders;
358
+ }
359
+ }
360
+ mcpPage.emulationSettings = Object.keys(newSettings).length
361
+ ? newSettings
362
+ : {};
363
+ this.#updateSelectedPageTimeouts();
364
+ // This should happen after updating the page timeouts.
365
+ // Setting the viewport can trigger a reload which we don't want to timeout.
366
+ await page.setViewport(newSettings.viewport ?? null);
367
+ }
368
+ setIsRunningPerformanceTrace(x) {
369
+ this.#isRunningTrace = x;
370
+ }
371
+ isRunningPerformanceTrace() {
372
+ return this.#isRunningTrace;
373
+ }
374
+ getScreenRecorder() {
375
+ return this.#screenRecorderData;
376
+ }
377
+ setScreenRecorder(data) {
378
+ this.#screenRecorderData = data;
379
+ }
380
+ isCruxEnabled() {
381
+ return this.#options.performanceCrux;
382
+ }
383
+ getSelectedPptrPage() {
384
+ const page = this.#selectedPage;
385
+ if (!page) {
386
+ throw new Error('No page selected');
387
+ }
388
+ if (page.pptrPage.isClosed()) {
389
+ throw new Error(`The selected page has been closed. Call ${listPages().name} to see open pages.`);
390
+ }
391
+ return page.pptrPage;
392
+ }
393
+ getSelectedMcpPage() {
394
+ const page = this.getSelectedPptrPage();
395
+ return this.#getMcpPage(page);
396
+ }
397
+ getPageById(pageId) {
398
+ const page = this.#mcpPages.values().find(mcpPage => mcpPage.id === pageId);
399
+ if (!page) {
400
+ throw new Error('No page found');
401
+ }
402
+ return page;
403
+ }
404
+ getPageId(page) {
405
+ return this.#mcpPages.get(page)?.id;
406
+ }
407
+ #getMcpPage(page) {
408
+ const mcpPage = this.#mcpPages.get(page);
409
+ if (!mcpPage) {
410
+ throw new Error('No McpPage found for the given page.');
411
+ }
412
+ return mcpPage;
413
+ }
414
+ #getSelectedMcpPage() {
415
+ return this.#getMcpPage(this.getSelectedPptrPage());
416
+ }
417
+ isPageSelected(page) {
418
+ return this.#selectedPage?.pptrPage === page;
419
+ }
420
+ selectPage(newPage) {
421
+ this.#selectedPage = newPage;
422
+ this.#updateSelectedPageTimeouts();
423
+ }
424
+ #updateSelectedPageTimeouts() {
425
+ const page = this.#getSelectedMcpPage();
426
+ // For waiters 5sec timeout should be sufficient.
427
+ // Increased in case we throttle the CPU
428
+ const cpuMultiplier = page.cpuThrottlingRate;
429
+ page.pptrPage.setDefaultTimeout(DEFAULT_TIMEOUT * cpuMultiplier);
430
+ // 10sec should be enough for the load event to be emitted during
431
+ // navigations.
432
+ // Increased in case we throttle the network requests or the CPU
433
+ const networkMultiplier = getNetworkMultiplierFromString(page.networkConditions);
434
+ page.pptrPage.setDefaultNavigationTimeout(NAVIGATION_TIMEOUT * networkMultiplier * cpuMultiplier);
435
+ }
436
+ // Linear scan over per-page snapshots. The page count is small (typically
437
+ // 2-10) so a reverse index isn't worthwhile given the uid-reuse lifecycle
438
+ // complexity it would introduce.
439
+ getAXNodeByUid(uid) {
440
+ for (const mcpPage of this.#mcpPages.values()) {
441
+ const node = mcpPage.textSnapshot?.idToNode.get(uid);
442
+ if (node) {
443
+ return node;
444
+ }
445
+ }
446
+ return undefined;
447
+ }
448
+ /**
449
+ * Creates a snapshot of the extension service workers.
450
+ */
451
+ async createExtensionServiceWorkersSnapshot() {
452
+ const allTargets = await this.browser.targets();
453
+ const serviceWorkers = allTargets.filter(target => {
454
+ return (target.type() === 'service_worker' &&
455
+ target.url().includes('chrome-extension://'));
456
+ });
457
+ for (const serviceWorker of serviceWorkers) {
458
+ if (!this.#extensionServiceWorkerMap.has(serviceWorker)) {
459
+ this.#extensionServiceWorkerMap.set(serviceWorker, 'sw-' + this.#nextExtensionServiceWorkerId++);
460
+ }
461
+ }
462
+ this.#extensionServiceWorkers = serviceWorkers.map(serviceWorker => {
463
+ return {
464
+ target: serviceWorker,
465
+ id: this.#extensionServiceWorkerMap.get(serviceWorker),
466
+ url: serviceWorker.url(),
467
+ };
468
+ });
469
+ return this.#extensionServiceWorkers;
470
+ }
471
+ getServiceWorkerConsoleData(extensionId) {
472
+ return this.#serviceWorkerConsoleCollector.getData(extensionId);
473
+ }
474
+ async createPagesSnapshot() {
475
+ const { pages: allPages, isolatedContextNames } = await this.#getAllPages();
476
+ for (const page of allPages) {
477
+ let mcpPage = this.#mcpPages.get(page);
478
+ if (!mcpPage) {
479
+ mcpPage = new McpPage(page, this.#nextPageId++);
480
+ this.#mcpPages.set(page, mcpPage);
481
+ // We emulate a focused page for all pages to support multi-agent workflows.
482
+ void page.emulateFocusedPage(true).catch(error => {
483
+ this.logger?.('Error turning on focused page emulation', error);
484
+ });
485
+ }
486
+ mcpPage.isolatedContextName = isolatedContextNames.get(page);
487
+ }
488
+ // Prune orphaned #mcpPages entries (pages that no longer exist).
489
+ const currentPages = new Set(allPages);
490
+ for (const [page, mcpPage] of this.#mcpPages) {
491
+ if (!currentPages.has(page)) {
492
+ mcpPage.dispose();
493
+ this.#mcpPages.delete(page);
494
+ }
495
+ }
496
+ this.#pages = allPages.filter(page => {
497
+ return (this.#options.experimentalDevToolsDebugging ||
498
+ !page.url().startsWith('devtools://'));
499
+ });
500
+ if ((!this.#selectedPage ||
501
+ this.#pages.indexOf(this.#selectedPage.pptrPage) === -1) &&
502
+ this.#pages[0]) {
503
+ this.selectPage(this.#getMcpPage(this.#pages[0]));
504
+ }
505
+ await this.detectOpenDevToolsWindows();
506
+ return this.#pages;
507
+ }
508
+ async #getAllPages() {
509
+ const defaultCtx = this.browser.defaultBrowserContext();
510
+ const allPages = await this.browser.pages(this.#options.experimentalIncludeAllPages);
511
+ const allTargets = this.browser.targets();
512
+ const extensionTargets = allTargets.filter(target => {
513
+ return (target.url().startsWith('chrome-extension://') &&
514
+ target.type() === 'page');
515
+ });
516
+ for (const target of extensionTargets) {
517
+ // Right now target.page() returns null for popup and side panel pages.
518
+ let page = await target.page();
519
+ if (!page) {
520
+ // We need to cache pages instances for targets because target.asPage()
521
+ // returns a new page instance every time.
522
+ page = this.#extensionPages.get(target) ?? null;
523
+ if (!page) {
524
+ try {
525
+ page = await target.asPage();
526
+ this.#extensionPages.set(target, page);
527
+ }
528
+ catch (e) {
529
+ this.logger?.('Failed to get page for extension target', e);
530
+ }
531
+ }
532
+ }
533
+ if (page && !allPages.includes(page)) {
534
+ allPages.push(page);
535
+ }
536
+ }
537
+ // Build a reverse lookup from BrowserContext instance → name.
538
+ const contextToName = new Map();
539
+ for (const [name, ctx] of this.#isolatedContexts) {
540
+ contextToName.set(ctx, name);
541
+ }
542
+ // Auto-discover BrowserContexts not in our mapping (e.g., externally
543
+ // created incognito contexts) and assign generated names.
544
+ const knownContexts = new Set(this.#isolatedContexts.values());
545
+ for (const ctx of this.browser.browserContexts()) {
546
+ if (ctx !== defaultCtx && !ctx.closed && !knownContexts.has(ctx)) {
547
+ const name = `isolated-context-${this.#nextIsolatedContextId++}`;
548
+ this.#isolatedContexts.set(name, ctx);
549
+ contextToName.set(ctx, name);
550
+ }
551
+ }
552
+ // Map each page to its isolated context name (if any).
553
+ const isolatedContextNames = new Map();
554
+ for (const page of allPages) {
555
+ const ctx = page.browserContext();
556
+ const name = contextToName.get(ctx);
557
+ if (name) {
558
+ isolatedContextNames.set(page, name);
559
+ }
560
+ }
561
+ return { pages: allPages, isolatedContextNames };
562
+ }
563
+ async detectOpenDevToolsWindows() {
564
+ this.logger?.('Detecting open DevTools windows');
565
+ const { pages } = await this.#getAllPages();
566
+ await Promise.all(pages.map(async (page) => {
567
+ const mcpPage = this.#mcpPages.get(page);
568
+ if (!mcpPage) {
569
+ return;
570
+ }
571
+ // Prior to Chrome 144.0.7559.59, the command fails,
572
+ // Some Electron apps still use older version
573
+ // Fall back to not exposing DevTools at all.
574
+ try {
575
+ if (await page.hasDevTools()) {
576
+ mcpPage.devToolsPage = await page.openDevTools();
577
+ }
578
+ else {
579
+ mcpPage.devToolsPage = undefined;
580
+ }
581
+ }
582
+ catch {
583
+ mcpPage.devToolsPage = undefined;
584
+ }
585
+ }));
586
+ }
587
+ getExtensionServiceWorkers() {
588
+ return this.#extensionServiceWorkers;
589
+ }
590
+ getExtensionServiceWorkerId(extensionServiceWorker) {
591
+ return this.#extensionServiceWorkerMap.get(extensionServiceWorker.target);
592
+ }
593
+ getPages() {
594
+ return this.#pages;
595
+ }
596
+ getIsolatedContextName(page) {
597
+ return this.#mcpPages.get(page)?.isolatedContextName;
598
+ }
599
+ async saveTemporaryFile(data, filename) {
600
+ const filepath = await getTempFilePath(filename);
601
+ await this.validatePath(filepath);
602
+ try {
603
+ await fs.writeFile(filepath, data);
604
+ }
605
+ catch (err) {
606
+ throw new Error('Could not save a file', { cause: err });
607
+ }
608
+ return { filepath };
609
+ }
610
+ async saveFile(data, clientProvidedFilePath, extension) {
611
+ await this.validatePath(clientProvidedFilePath);
612
+ try {
613
+ const filePath = ensureExtension(path.resolve(clientProvidedFilePath), extension);
614
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
615
+ await fs.writeFile(filePath, data);
616
+ return { filename: filePath };
617
+ }
618
+ catch (err) {
619
+ this.logger?.(err);
620
+ throw new Error('Could not save a file', { cause: err });
621
+ }
622
+ }
623
+ storeTraceRecording(result) {
624
+ // Clear the trace results because we only consume the latest trace currently.
625
+ this.#traceResults = [];
626
+ this.#traceResults.push(result);
627
+ }
628
+ recordedTraces() {
629
+ return this.#traceResults;
630
+ }
631
+ getNetworkRequestStableId(request) {
632
+ return this.#networkCollector.getIdForResource(request);
633
+ }
634
+ waitForTextOnPage(text, timeout, targetPage) {
635
+ const page = targetPage ?? this.getSelectedPptrPage();
636
+ const frames = page.frames();
637
+ let locator = this.#locatorClass.race(frames.flatMap(frame => text.flatMap(value => [
638
+ frame.locator(`aria/${value}`),
639
+ frame.locator(`text/${value}`),
640
+ ])));
641
+ if (timeout) {
642
+ locator = locator.setTimeout(timeout);
643
+ }
644
+ return locator.wait();
645
+ }
646
+ /**
647
+ * We need to ignore favicon request as they make our test flaky
648
+ */
649
+ async setUpNetworkCollectorForTesting() {
650
+ this.#networkCollector = new NetworkCollector(this.browser, collect => {
651
+ return {
652
+ request: req => {
653
+ if (req.url().includes('favicon.ico')) {
654
+ return;
655
+ }
656
+ collect(req);
657
+ },
658
+ };
659
+ });
660
+ const { pages } = await this.#getAllPages();
661
+ await this.#networkCollector.init(pages);
662
+ }
663
+ async installExtension(extensionPath) {
664
+ const id = await this.browser.installExtension(extensionPath);
665
+ return id;
666
+ }
667
+ async uninstallExtension(id) {
668
+ await this.browser.uninstallExtension(id);
669
+ }
670
+ async triggerExtensionAction(id) {
671
+ const extensions = await this.browser.extensions();
672
+ const extension = extensions.get(id);
673
+ if (!extension) {
674
+ throw new Error(`Extension with ID ${id} not found.`);
675
+ }
676
+ const page = this.getSelectedPptrPage();
677
+ await extension.triggerAction(page);
678
+ }
679
+ listExtensions() {
680
+ return this.browser.extensions();
681
+ }
682
+ async getExtension(id) {
683
+ const pptrExtensions = await this.browser.extensions();
684
+ return pptrExtensions.get(id);
685
+ }
686
+ async getHeapSnapshotAggregates(filePath) {
687
+ return await this.#heapSnapshotManager.getAggregates(filePath);
688
+ }
689
+ async getHeapSnapshotStats(filePath) {
690
+ return await this.#heapSnapshotManager.getStats(filePath);
691
+ }
692
+ async getHeapSnapshotStaticData(filePath) {
693
+ return await this.#heapSnapshotManager.getStaticData(filePath);
694
+ }
695
+ async getHeapSnapshotNodesById(filePath, id) {
696
+ return await this.#heapSnapshotManager.getNodesById(filePath, id);
697
+ }
698
+ async getHeapSnapshotRetainers(filePath, nodeId) {
699
+ return await this.#heapSnapshotManager.getRetainers(filePath, nodeId);
700
+ }
701
+ async closeHeapSnapshot(filePath) {
702
+ return this.#heapSnapshotManager.dispose(filePath);
703
+ }
704
+ hasHeapSnapshots() {
705
+ return this.#heapSnapshotManager.hasSnapshots();
706
+ }
707
+ async getHeapSnapshotRetainingPaths(filePath, nodeId, maxDepth, maxNodes, maxSiblings) {
708
+ return await this.#heapSnapshotManager.getRetainingPaths(filePath, nodeId, maxDepth, maxNodes, maxSiblings);
709
+ }
710
+ async getHeapSnapshotDominators(filePath, nodeId) {
711
+ return await this.#heapSnapshotManager.getDominatorsOf(filePath, nodeId);
712
+ }
713
+ async loadResource(path) {
714
+ const url = new URL(path);
715
+ switch (url.protocol) {
716
+ case 'https:':
717
+ case 'http:': {
718
+ // TODO: Verify allow/block list
719
+ const response = await fetch(url);
720
+ if (!response.ok) {
721
+ throw new Error(`Failed to load resource: ${url}`);
722
+ }
723
+ return response.text();
724
+ }
725
+ case 'file:': {
726
+ await this.validatePath(fileURLToPath(url));
727
+ return await fsPromises.readFile(url, 'utf-8');
728
+ }
729
+ default:
730
+ throw new Error(`Unsupported protocol for: ${url}`);
731
+ }
732
+ }
733
+ async getHeapSnapshotEdges(filePath, nodeId) {
734
+ return await this.#heapSnapshotManager.getEdges(filePath, nodeId);
735
+ }
736
+ }
737
+ //# sourceMappingURL=McpContext.js.map