@yezack/chrome-devtools-mcp-108 0.0.0-stage → 1.10.2

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 (360) hide show
  1. package/FORK-NOTICE.md +45 -0
  2. package/LICENSE +202 -0
  3. package/README.md +45 -2
  4. package/build/src/BrowserManager.js +401 -0
  5. package/build/src/McpContext.js +642 -0
  6. package/build/src/McpPage.js +850 -0
  7. package/build/src/McpResponse.js +1086 -0
  8. package/build/src/SlimMcpResponse.js +19 -0
  9. package/build/src/TextSnapshot.js +315 -0
  10. package/build/src/ToolHandler.js +322 -0
  11. package/build/src/bin/check-latest-version.js +51 -0
  12. package/build/src/bin/chrome-devtools-mcp-main.js +77 -0
  13. package/build/src/bin/chrome-devtools-mcp.js +23 -0
  14. package/build/src/bin/chrome-devtools.js +222 -0
  15. package/build/src/collectors/PageCollector.js +291 -0
  16. package/build/src/collectors/ServiceWorkerCollector.js +171 -0
  17. package/build/src/config/browser-options.js +125 -0
  18. package/build/src/config/category-options.js +89 -0
  19. package/build/src/config/cli-commands.js +26 -0
  20. package/build/src/config/cli-options.js +1473 -0
  21. package/build/src/config/mcp-options.js +417 -0
  22. package/build/src/daemon/client.js +221 -0
  23. package/build/src/daemon/daemon.js +266 -0
  24. package/build/src/daemon/types.js +7 -0
  25. package/build/src/daemon/utils.js +136 -0
  26. package/build/src/devtools/DevToolsCommentBridge.js +91 -0
  27. package/build/src/devtools/DevtoolsUtils.js +307 -0
  28. package/build/src/devtools/McpHostBindingAdapter.js +165 -0
  29. package/build/src/devtools/issueDescriptions.js +42 -0
  30. package/build/src/formatters/CommentFormatter.js +82 -0
  31. package/build/src/formatters/ConsoleFormatter.js +321 -0
  32. package/build/src/formatters/CssFormatter.js +812 -0
  33. package/build/src/formatters/HeapSnapshotFormatter.js +185 -0
  34. package/build/src/formatters/IssueFormatter.js +193 -0
  35. package/build/src/formatters/NetworkFormatter.js +241 -0
  36. package/build/src/formatters/SnapshotFormatter.js +135 -0
  37. package/build/src/index.js +237 -0
  38. package/build/src/processors/ChunkedTraceParser.js +361 -0
  39. package/build/src/processors/HeapSnapshotManager.js +270 -0
  40. package/build/src/processors/PerformanceTrace.js +132 -0
  41. package/build/src/telemetry/ClearcutLogger.js +242 -0
  42. package/build/src/telemetry/WatchdogClient.js +61 -0
  43. package/build/src/telemetry/errors.js +18 -0
  44. package/build/src/telemetry/flagUtils.js +90 -0
  45. package/build/src/telemetry/metricsRegistry.js +85 -0
  46. package/build/src/telemetry/persistence.js +82 -0
  47. package/build/src/telemetry/transformation.js +233 -0
  48. package/build/src/telemetry/types.js +36 -0
  49. package/build/src/telemetry/watchdog/ClearcutSender.js +205 -0
  50. package/build/src/telemetry/watchdog/main.js +128 -0
  51. package/build/src/third_party/THIRD_PARTY_NOTICES +4077 -0
  52. package/build/src/third_party/bundled-packages.json +12 -0
  53. package/build/src/third_party/devtools-formatter-worker.js +15494 -0
  54. package/build/src/third_party/devtools-heap-snapshot-worker.js +10979 -0
  55. package/build/src/third_party/index.js +200978 -0
  56. package/build/src/third_party/issue-descriptions/CoepCoopSandboxedIframeCannotNavigateToCoopPage.md +4 -0
  57. package/build/src/third_party/issue-descriptions/CoepCorpNotSameOrigin.md +8 -0
  58. package/build/src/third_party/issue-descriptions/CoepCorpNotSameOriginAfterDefaultedToSameOriginByCoep.md +18 -0
  59. package/build/src/third_party/issue-descriptions/CoepCorpNotSameSite.md +7 -0
  60. package/build/src/third_party/issue-descriptions/CoepFrameResourceNeedsCoepHeader.md +10 -0
  61. package/build/src/third_party/issue-descriptions/CompatibilityModeQuirks.md +5 -0
  62. package/build/src/third_party/issue-descriptions/CookieAttributeValueExceedsMaxSize.md +5 -0
  63. package/build/src/third_party/issue-descriptions/LowTextContrast.md +5 -0
  64. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorRead.md +8 -0
  65. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorSet.md +8 -0
  66. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnRead.md +8 -0
  67. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnSet.md +8 -0
  68. package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeRead.md +9 -0
  69. package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeSet.md +9 -0
  70. package/build/src/third_party/issue-descriptions/bounceTrackingMitigations.md +3 -0
  71. package/build/src/third_party/issue-descriptions/clientHintMetaTagAllowListInvalidOrigin.md +4 -0
  72. package/build/src/third_party/issue-descriptions/clientHintMetaTagModifiedHTML.md +4 -0
  73. package/build/src/third_party/issue-descriptions/connectionAllowlistEmbeddingRequirementNotSatisfied.md +26 -0
  74. package/build/src/third_party/issue-descriptions/connectionAllowlistIFrameAttributeLoosensEmbeddingRequirement.md +19 -0
  75. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowConnectionAllowlistFrom.md +21 -0
  76. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowlistItemType.md +12 -0
  77. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidHeader.md +12 -0
  78. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidUrlPattern.md +8 -0
  79. package/build/src/third_party/issue-descriptions/connectionAllowlistItemNotInnerList.md +12 -0
  80. package/build/src/third_party/issue-descriptions/connectionAllowlistMoreThanOneList.md +7 -0
  81. package/build/src/third_party/issue-descriptions/connectionAllowlistReportingEndpointNotToken.md +10 -0
  82. package/build/src/third_party/issue-descriptions/cookieCrossSiteRedirectDowngrade.md +12 -0
  83. package/build/src/third_party/issue-descriptions/cookieExcludeDomainNonAscii.md +11 -0
  84. package/build/src/third_party/issue-descriptions/cookieExcludePortMismatch.md +8 -0
  85. package/build/src/third_party/issue-descriptions/cookieExcludeSchemeMismatch.md +7 -0
  86. package/build/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutRead.md +6 -0
  87. package/build/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutSet.md +6 -0
  88. package/build/src/third_party/issue-descriptions/cookieWarnDomainNonAscii.md +11 -0
  89. package/build/src/third_party/issue-descriptions/cookieWarnMetadataGrantRead.md +4 -0
  90. package/build/src/third_party/issue-descriptions/cookieWarnMetadataGrantSet.md +4 -0
  91. package/build/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutRead.md +6 -0
  92. package/build/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutSet.md +6 -0
  93. package/build/src/third_party/issue-descriptions/corsAllowCredentialsRequired.md +6 -0
  94. package/build/src/third_party/issue-descriptions/corsDisabledScheme.md +7 -0
  95. package/build/src/third_party/issue-descriptions/corsDisallowedByMode.md +7 -0
  96. package/build/src/third_party/issue-descriptions/corsHeaderDisallowedByPreflightResponse.md +5 -0
  97. package/build/src/third_party/issue-descriptions/corsInvalidHeaderValues.md +7 -0
  98. package/build/src/third_party/issue-descriptions/corsLocalNetworkAccessPermissionDenied.md +19 -0
  99. package/build/src/third_party/issue-descriptions/corsMethodDisallowedByPreflightResponse.md +5 -0
  100. package/build/src/third_party/issue-descriptions/corsNoCorsRedirectModeNotFollow.md +5 -0
  101. package/build/src/third_party/issue-descriptions/corsOriginMismatch.md +6 -0
  102. package/build/src/third_party/issue-descriptions/corsPreflightResponseInvalid.md +5 -0
  103. package/build/src/third_party/issue-descriptions/corsRedirectContainsCredentials.md +5 -0
  104. package/build/src/third_party/issue-descriptions/corsWildcardOriginNotAllowed.md +9 -0
  105. package/build/src/third_party/issue-descriptions/cspEvalViolation.md +9 -0
  106. package/build/src/third_party/issue-descriptions/cspInlineViolation.md +10 -0
  107. package/build/src/third_party/issue-descriptions/cspTrustedTypesPolicyViolation.md +5 -0
  108. package/build/src/third_party/issue-descriptions/cspTrustedTypesSinkViolation.md +8 -0
  109. package/build/src/third_party/issue-descriptions/cspURLViolation.md +10 -0
  110. package/build/src/third_party/issue-descriptions/deprecation.md +3 -0
  111. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsEmptyList.md +1 -0
  112. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsHttpNotFound.md +1 -0
  113. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidContentType.md +1 -0
  114. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidResponse.md +1 -0
  115. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsNoResponse.md +1 -0
  116. package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsFetchFailed.md +1 -0
  117. package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsInvalidRecord.md +1 -0
  118. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownHttpNotFound.md +1 -0
  119. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidContentType.md +1 -0
  120. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidResponse.md +1 -0
  121. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownNoResponse.md +1 -0
  122. package/build/src/third_party/issue-descriptions/emailVerificationRequestInvalidEmail.md +1 -0
  123. package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksHttpNotFound.md +1 -0
  124. package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksInvalidResponse.md +1 -0
  125. package/build/src/third_party/issue-descriptions/emailVerificationRequestKeyBindingSigningFailed.md +1 -0
  126. package/build/src/third_party/issue-descriptions/emailVerificationRequestRpOriginIsOpaque.md +1 -0
  127. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenHttpNotFound.md +1 -0
  128. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidContentType.md +1 -0
  129. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidResponse.md +1 -0
  130. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidSdJwt.md +1 -0
  131. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenMalformedSdJwt.md +1 -0
  132. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenNoResponse.md +1 -0
  133. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidAudience.md +1 -0
  134. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidIssuedAt.md +1 -0
  135. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidNonce.md +1 -0
  136. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidSdHash.md +1 -0
  137. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidTyp.md +1 -0
  138. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingAud.md +1 -0
  139. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingCnf.md +1 -0
  140. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingIat.md +1 -0
  141. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingNonce.md +1 -0
  142. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingSdHash.md +1 -0
  143. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbSignatureFailed.md +1 -0
  144. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmail.md +1 -0
  145. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmailVerified.md +1 -0
  146. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidHolderKey.md +1 -0
  147. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuedAt.md +1 -0
  148. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuer.md +1 -0
  149. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtJwksMissingKeys.md +1 -0
  150. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingCnf.md +1 -0
  151. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingEmail.md +1 -0
  152. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIat.md +1 -0
  153. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIss.md +1 -0
  154. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtSignatureFailed.md +1 -0
  155. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtUnsupportedHeaderAlg.md +1 -0
  156. package/build/src/third_party/issue-descriptions/emailVerificationRequestUserLoggedOut.md +1 -0
  157. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownAccountsEndpointCrossOrigin.md +1 -0
  158. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownHttpNotFound.md +1 -0
  159. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidContentType.md +1 -0
  160. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidResponse.md +1 -0
  161. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownIssuanceEndpointCrossOrigin.md +1 -0
  162. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownListEmpty.md +1 -0
  163. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingAccountsEndpoint.md +1 -0
  164. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingIssuanceEndpoint.md +1 -0
  165. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownNoResponse.md +1 -0
  166. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownUnsupportedSigningAlgorithm.md +1 -0
  167. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsBlockedByConnectionAllowlist.md +1 -0
  168. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsHttpNotFound.md +1 -0
  169. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsInvalidResponse.md +1 -0
  170. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsNoResponse.md +1 -0
  171. package/build/src/third_party/issue-descriptions/federatedAuthRequestApprovalDeclined.md +1 -0
  172. package/build/src/third_party/issue-descriptions/federatedAuthRequestCanceled.md +1 -0
  173. package/build/src/third_party/issue-descriptions/federatedAuthRequestConfigBlockedByConnectionAllowlist.md +1 -0
  174. package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorFetchingSignin.md +1 -0
  175. package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorIdToken.md +1 -0
  176. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenBlockedByConnectionAllowlist.md +1 -0
  177. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenHttpNotFound.md +1 -0
  178. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidRequest.md +1 -0
  179. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidResponse.md +1 -0
  180. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenNoResponse.md +1 -0
  181. package/build/src/third_party/issue-descriptions/federatedAuthRequestInvalidSigninResponse.md +1 -0
  182. package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestHttpNotFound.md +1 -0
  183. package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestInvalidResponse.md +1 -0
  184. package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestNoResponse.md +1 -0
  185. package/build/src/third_party/issue-descriptions/federatedAuthRequestTooManyRequests.md +1 -0
  186. package/build/src/third_party/issue-descriptions/federatedAuthRequestWellKnownBlockedByConnectionAllowlist.md +1 -0
  187. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidAccountsResponse.md +1 -0
  188. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidConfigOrWellKnown.md +1 -0
  189. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoAccountSharingPermission.md +1 -0
  190. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoApiPermission.md +1 -0
  191. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoReturningUserFromFetchedAccounts.md +1 -0
  192. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotIframe.md +1 -0
  193. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotPotentiallyTrustworthy.md +1 -0
  194. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSameOrigin.md +1 -0
  195. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSignedInWithIdp.md +1 -0
  196. package/build/src/third_party/issue-descriptions/fetchingPartitionedBlobURL.md +7 -0
  197. package/build/src/third_party/issue-descriptions/genericBackUINavigationWouldSkipAd.md +4 -0
  198. package/build/src/third_party/issue-descriptions/genericFormAriaLabelledByToNonExistingIdError.md +8 -0
  199. package/build/src/third_party/issue-descriptions/genericFormAutocompleteAttributeEmptyError.md +5 -0
  200. package/build/src/third_party/issue-descriptions/genericFormDuplicateIdForInputError.md +5 -0
  201. package/build/src/third_party/issue-descriptions/genericFormEmptyIdAndNameAttributesForInputError.md +5 -0
  202. package/build/src/third_party/issue-descriptions/genericFormInputAssignedAutocompleteValueToIdOrNameAttributeError.md +5 -0
  203. package/build/src/third_party/issue-descriptions/genericFormInputHasWrongButWellIntendedAutocompleteValueError.md +5 -0
  204. package/build/src/third_party/issue-descriptions/genericFormInputWithNoLabelError.md +5 -0
  205. package/build/src/third_party/issue-descriptions/genericFormLabelForMatchesNonExistingIdError.md +5 -0
  206. package/build/src/third_party/issue-descriptions/genericFormLabelForNameError.md +5 -0
  207. package/build/src/third_party/issue-descriptions/genericFormLabelHasNeitherForNorNestedInputError.md +5 -0
  208. package/build/src/third_party/issue-descriptions/genericFormModelContextMissingToolDescription.md +5 -0
  209. package/build/src/third_party/issue-descriptions/genericFormModelContextMissingToolName.md +5 -0
  210. package/build/src/third_party/issue-descriptions/genericFormModelContextParameterMissingName.md +5 -0
  211. package/build/src/third_party/issue-descriptions/genericFormModelContextParameterMissingTitleAndDescription.md +5 -0
  212. package/build/src/third_party/issue-descriptions/genericFormModelContextRequiredParameterMissingName.md +5 -0
  213. package/build/src/third_party/issue-descriptions/genericNavigationEntryMarkedSkippable.md +7 -0
  214. package/build/src/third_party/issue-descriptions/genericResponseWasBlockedByORB.md +4 -0
  215. package/build/src/third_party/issue-descriptions/heavyAd.md +10 -0
  216. package/build/src/third_party/issue-descriptions/lazyLoadImageZeroSize.md +7 -0
  217. package/build/src/third_party/issue-descriptions/mixedContent.md +5 -0
  218. package/build/src/third_party/issue-descriptions/navigatingPartitionedBlobURL.md +5 -0
  219. package/build/src/third_party/issue-descriptions/permissionElementActivationDisabled.md +7 -0
  220. package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluder.md +9 -0
  221. package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluderParent.md +9 -0
  222. package/build/src/third_party/issue-descriptions/permissionElementCspFrameAncestorsMissing.md +5 -0
  223. package/build/src/third_party/issue-descriptions/permissionElementFencedFrameDisallowed.md +5 -0
  224. package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooLarge.md +5 -0
  225. package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooSmall.md +5 -0
  226. package/build/src/third_party/issue-descriptions/permissionElementGeolocationDeprecated.md +5 -0
  227. package/build/src/third_party/issue-descriptions/permissionElementInsetBoxShadowUnsupported.md +5 -0
  228. package/build/src/third_party/issue-descriptions/permissionElementInvalidDisplayStyle.md +5 -0
  229. package/build/src/third_party/issue-descriptions/permissionElementInvalidSizeValue.md +5 -0
  230. package/build/src/third_party/issue-descriptions/permissionElementInvalidType.md +5 -0
  231. package/build/src/third_party/issue-descriptions/permissionElementInvalidTypeActivation.md +5 -0
  232. package/build/src/third_party/issue-descriptions/permissionElementLowContrast.md +5 -0
  233. package/build/src/third_party/issue-descriptions/permissionElementNonOpaqueColor.md +5 -0
  234. package/build/src/third_party/issue-descriptions/permissionElementPaddingBottomUnsupported.md +6 -0
  235. package/build/src/third_party/issue-descriptions/permissionElementPaddingRightUnsupported.md +6 -0
  236. package/build/src/third_party/issue-descriptions/permissionElementPermissionsPolicyBlocked.md +5 -0
  237. package/build/src/third_party/issue-descriptions/permissionElementRegistrationFailed.md +5 -0
  238. package/build/src/third_party/issue-descriptions/permissionElementRequestInProgress.md +5 -0
  239. package/build/src/third_party/issue-descriptions/permissionElementSecurityChecksFailed.md +5 -0
  240. package/build/src/third_party/issue-descriptions/permissionElementTypeNotSupported.md +5 -0
  241. package/build/src/third_party/issue-descriptions/permissionElementUntrustedEvent.md +7 -0
  242. package/build/src/third_party/issue-descriptions/placeholderDescriptionForInvisibleIssues.md +3 -0
  243. package/build/src/third_party/issue-descriptions/propertyRuleInvalidNameIssue.md +3 -0
  244. package/build/src/third_party/issue-descriptions/propertyRuleIssue.md +7 -0
  245. package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedOptGroupChild.md +7 -0
  246. package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedSelectChild.md +7 -0
  247. package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentAttributesSelectDescendant.md +3 -0
  248. package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentLegendChild.md +3 -0
  249. package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentOptionChild.md +3 -0
  250. package/build/src/third_party/issue-descriptions/selectElementAccessibilityNonPhrasingContentOptionChild.md +3 -0
  251. package/build/src/third_party/issue-descriptions/selectivePermissionsIntervention.md +7 -0
  252. package/build/src/third_party/issue-descriptions/sharedArrayBuffer.md +7 -0
  253. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorCrossOriginNoCorsRequest.md +1 -0
  254. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorDictionaryLoadFailure.md +3 -0
  255. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorMatchingDictionaryNotUsed.md +3 -0
  256. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorUnexpectedContentDictionaryHeader.md +1 -0
  257. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorCossOriginNoCorsRequest.md +1 -0
  258. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorDisallowedBySettings.md +1 -0
  259. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorExpiredResponse.md +3 -0
  260. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorFeatureDisabled.md +3 -0
  261. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInsufficientResources.md +1 -0
  262. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidMatchField.md +1 -0
  263. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidStructuredHeader.md +1 -0
  264. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidTTLField.md +1 -0
  265. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNavigationRequest.md +3 -0
  266. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNoMatchField.md +1 -0
  267. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonIntegerTTLField.md +1 -0
  268. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonListMatchDestField.md +1 -0
  269. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonSecureContext.md +3 -0
  270. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringIdField.md +1 -0
  271. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringInMatchDestList.md +1 -0
  272. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonStringMatchField.md +1 -0
  273. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNonTokenTypeField.md +1 -0
  274. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorRequestAborted.md +1 -0
  275. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorShuttingDown.md +1 -0
  276. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorTooLongIdField.md +3 -0
  277. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorUnsupportedType.md +3 -0
  278. package/build/src/third_party/issue-descriptions/sriInvalidSignatureHeader.md +14 -0
  279. package/build/src/third_party/issue-descriptions/sriInvalidSignatureInputHeader.md +15 -0
  280. package/build/src/third_party/issue-descriptions/sriMissingSignatureHeader.md +8 -0
  281. package/build/src/third_party/issue-descriptions/sriMissingSignatureInputHeader.md +7 -0
  282. package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsIncorrectLength.md +11 -0
  283. package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsNotByteSequence.md +14 -0
  284. package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsParameterized.md +15 -0
  285. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentName.md +8 -0
  286. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentType.md +13 -0
  287. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidDerivedComponentParameter.md +4 -0
  288. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidHeaderComponentParameter.md +5 -0
  289. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidParameter.md +11 -0
  290. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderKeyIdLength.md +12 -0
  291. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingLabel.md +6 -0
  292. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingRequiredParameters.md +8 -0
  293. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueMissingComponents.md +11 -0
  294. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueNotInnerList.md +11 -0
  295. package/build/src/third_party/issue-descriptions/sriValidationFailedIntegrityMismatch.md +10 -0
  296. package/build/src/third_party/issue-descriptions/sriValidationFailedInvalidLength.md +5 -0
  297. package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureExpired.md +6 -0
  298. package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureMismatch.md +11 -0
  299. package/build/src/third_party/issue-descriptions/stylesheetLateImport.md +4 -0
  300. package/build/src/third_party/issue-descriptions/stylesheetRequestFailed.md +3 -0
  301. package/build/src/third_party/issue-descriptions/summaryElementAccessibilityInteractiveContentSummaryDescendant.md +3 -0
  302. package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestLength.md +12 -0
  303. package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestType.md +17 -0
  304. package/build/src/third_party/issue-descriptions/unencodedDigestMalformedDictionary.md +14 -0
  305. package/build/src/third_party/issue-descriptions/unencodedDigestUnknownAlgorithm.md +15 -0
  306. package/build/src/third_party/issue-descriptions/webInstallManifestMissingId.md +3 -0
  307. package/build/src/third_party/issue-descriptions/webInstallManifestMissingNameOrShortName.md +3 -0
  308. package/build/src/third_party/issue-descriptions/webInstallManifestParsingOrNetworkError.md +3 -0
  309. package/build/src/third_party/issue-descriptions/webInstallNoManifest.md +3 -0
  310. package/build/src/third_party/issue-descriptions/webInstallStartUrlInvalid.md +3 -0
  311. package/build/src/third_party/lighthouse-devtools-mcp-bundle.js +58435 -0
  312. package/build/src/tools/ToolDefinition.js +80 -0
  313. package/build/src/tools/categories.js +33 -0
  314. package/build/src/tools/comments.js +169 -0
  315. package/build/src/tools/console.js +104 -0
  316. package/build/src/tools/css.js +45 -0
  317. package/build/src/tools/emulation.js +84 -0
  318. package/build/src/tools/extensions.js +103 -0
  319. package/build/src/tools/input.js +652 -0
  320. package/build/src/tools/lighthouse.js +149 -0
  321. package/build/src/tools/memory.js +424 -0
  322. package/build/src/tools/network.js +128 -0
  323. package/build/src/tools/pages.js +363 -0
  324. package/build/src/tools/performance.js +212 -0
  325. package/build/src/tools/pwa.js +128 -0
  326. package/build/src/tools/screencast.js +119 -0
  327. package/build/src/tools/screenshot.js +277 -0
  328. package/build/src/tools/script.js +232 -0
  329. package/build/src/tools/slim/tools.js +96 -0
  330. package/build/src/tools/snapshot.js +63 -0
  331. package/build/src/tools/thirdPartyDeveloper.js +85 -0
  332. package/build/src/tools/tools.js +55 -0
  333. package/build/src/tools/webmcp.js +66 -0
  334. package/build/src/types.js +10 -0
  335. package/build/src/utils/WaitForHelper.js +316 -0
  336. package/build/src/utils/bytes.js +105 -0
  337. package/build/src/utils/check-for-updates.js +75 -0
  338. package/build/src/utils/files.js +57 -0
  339. package/build/src/utils/id.js +16 -0
  340. package/build/src/utils/keyboard.js +297 -0
  341. package/build/src/utils/logger.js +52 -0
  342. package/build/src/utils/pagination.js +50 -0
  343. package/build/src/utils/polyfill.js +11 -0
  344. package/build/src/utils/url.js +116 -0
  345. package/build/src/version.js +10 -0
  346. package/package.json +41 -4
  347. package/skills/a11y-debugging/SKILL.md +91 -0
  348. package/skills/a11y-debugging/references/a11y-snippets.md +92 -0
  349. package/skills/chrome-devtools/SKILL.md +74 -0
  350. package/skills/chrome-devtools-cli/SKILL.md +194 -0
  351. package/skills/chrome-devtools-cli/references/installation.md +14 -0
  352. package/skills/cookie-debugging/SKILL.md +149 -0
  353. package/skills/debug-optimize-lcp/SKILL.md +121 -0
  354. package/skills/debug-optimize-lcp/references/elements-and-size.md +27 -0
  355. package/skills/debug-optimize-lcp/references/lcp-breakdown.md +23 -0
  356. package/skills/debug-optimize-lcp/references/lcp-snippets.md +79 -0
  357. package/skills/debug-optimize-lcp/references/optimization-strategies.md +38 -0
  358. package/skills/memory-leak-debugging/SKILL.md +58 -0
  359. package/skills/memory-leak-debugging/references/common-leaks.md +33 -0
  360. package/skills/troubleshooting/SKILL.md +99 -0
@@ -0,0 +1,642 @@
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 os from 'node:os';
8
+ import path from 'node:path';
9
+ import { fileURLToPath, pathToFileURL } from 'node:url';
10
+ import { overrideDevToolsGlobals } from './devtools/DevtoolsUtils.js';
11
+ import { HeapSnapshotManager } from './processors/HeapSnapshotManager.js';
12
+ import { McpPage } from './McpPage.js';
13
+ import { ServiceWorkerConsoleCollector } from './collectors/ServiceWorkerCollector.js';
14
+ import { Locator, } from './third_party/index.js';
15
+ import { CLOSE_PAGE_ERROR } from './tools/ToolDefinition.js';
16
+ import { getTempFilePath, resolveCanonicalPath } from './utils/files.js';
17
+ import { isAllowedUrl } from './utils/url.js';
18
+ // Page ids are handed out from a process-wide counter so they stay unique
19
+ // across all contexts, in particular across browser reconnects. An id issued
20
+ // before a reconnect then fails to resolve instead of hitting an unrelated
21
+ // page of the reconnected browser.
22
+ let nextPageId = 1;
23
+ export class McpContext {
24
+ browser;
25
+ logger;
26
+ // Maps LLM-provided isolatedContext name → Puppeteer BrowserContext.
27
+ #isolatedContexts = new Map();
28
+ // Auto-generated name counter for when no name is provided.
29
+ #nextIsolatedContextId = 1;
30
+ #extensionServiceWorkers = [];
31
+ #mcpPages = new Map();
32
+ #selectedPage;
33
+ #selectedPageFallback;
34
+ #serviceWorkerConsoleCollector;
35
+ #isRunningTrace = false;
36
+ #screenRecorderData = null;
37
+ #reconnectNotice = false;
38
+ #extensionPages = new WeakMap();
39
+ #extensionServiceWorkerMap = new WeakMap();
40
+ #nextExtensionServiceWorkerId = 1;
41
+ #traceResults = [];
42
+ #locatorClass;
43
+ #options;
44
+ #heapSnapshotManager = new HeapSnapshotManager();
45
+ #roots = undefined;
46
+ #allowUnrestrictedPaths;
47
+ constructor(browser, logger, options, locatorClass) {
48
+ overrideDevToolsGlobals({
49
+ loadResource: (url) => {
50
+ return this.loadResource(url);
51
+ },
52
+ });
53
+ this.browser = browser;
54
+ this.logger = logger;
55
+ this.#locatorClass = locatorClass;
56
+ this.#options = options;
57
+ this.#allowUnrestrictedPaths = options.allowUnrestrictedPaths ?? false;
58
+ this.#reconnectNotice = options.reconnected ?? false;
59
+ this.#serviceWorkerConsoleCollector = new ServiceWorkerConsoleCollector(this.browser);
60
+ }
61
+ async #init() {
62
+ await this.createPagesSnapshot();
63
+ const workers = await this.createExtensionServiceWorkersSnapshot();
64
+ await this.#serviceWorkerConsoleCollector.init(workers);
65
+ this.browser.on('targetcreated', this.#onTargetCreated);
66
+ this.browser.on('targetdestroyed', this.#onTargetDestroyed);
67
+ }
68
+ dispose() {
69
+ this.browser.off('targetcreated', this.#onTargetCreated);
70
+ this.browser.off('targetdestroyed', this.#onTargetDestroyed);
71
+ this.#serviceWorkerConsoleCollector.dispose();
72
+ this.#heapSnapshotManager.dispose();
73
+ for (const mcpPage of this.#mcpPages.values()) {
74
+ mcpPage.dispose();
75
+ }
76
+ this.#mcpPages.clear();
77
+ // Isolated contexts are intentionally not closed here.
78
+ // Either the entire browser will be closed or we disconnect
79
+ // without destroying browser state.
80
+ this.#isolatedContexts.clear();
81
+ }
82
+ #onTargetCreated = async (target) => {
83
+ try {
84
+ const url = target.url();
85
+ if (!isAllowedUrl(url, {
86
+ categoryExtensions: this.#options.categoryExtensions,
87
+ })) {
88
+ return;
89
+ }
90
+ const page = await target.page();
91
+ if (!page) {
92
+ return;
93
+ }
94
+ void this.#createMcpPage(page);
95
+ }
96
+ catch (err) {
97
+ this.logger?.('Error handling targetcreated', err);
98
+ }
99
+ };
100
+ #onTargetDestroyed = (target) => {
101
+ try {
102
+ let foundPage;
103
+ for (const page of this.#mcpPages.keys()) {
104
+ if (page.target() === target) {
105
+ foundPage = page;
106
+ break;
107
+ }
108
+ }
109
+ if (!foundPage) {
110
+ return;
111
+ }
112
+ const mcpPage = this.#mcpPages.get(foundPage);
113
+ if (mcpPage) {
114
+ mcpPage.dispose();
115
+ this.#mcpPages.delete(foundPage);
116
+ }
117
+ }
118
+ catch (err) {
119
+ this.logger?.('Error handling targetdestroyed', err);
120
+ }
121
+ };
122
+ static async from(browser, logger, opts,
123
+ /* Let tests use unbundled Locator class to avoid overly strict checks within puppeteer that fail when mixing bundled and unbundled class instances */
124
+ locatorClass = Locator) {
125
+ const context = new McpContext(browser, logger, opts, locatorClass);
126
+ await context.#init();
127
+ return context;
128
+ }
129
+ static resetPageIdsForTesting() {
130
+ nextPageId = 1;
131
+ }
132
+ roots() {
133
+ return [
134
+ ...(this.#roots ?? []),
135
+ {
136
+ uri: pathToFileURL(os.tmpdir()).href,
137
+ name: 'temp',
138
+ },
139
+ ];
140
+ }
141
+ setRoots(roots) {
142
+ this.#roots = roots;
143
+ }
144
+ async validatePath(filePath) {
145
+ if (filePath === undefined) {
146
+ return undefined;
147
+ }
148
+ let canonicalPath;
149
+ try {
150
+ canonicalPath = await resolveCanonicalPath(filePath);
151
+ }
152
+ catch (err) {
153
+ const errMsg = err instanceof Error ? err.message : String(err);
154
+ console.error(`[MCP Context] Error resolving real path for ${filePath}: ${errMsg}`);
155
+ throw new Error(`Access denied: Cannot resolve base path for ${filePath}.`);
156
+ }
157
+ // If the client never negotiated roots and the operator has explicitly
158
+ // opted into unrestricted access via --allow-unrestricted-paths, restore
159
+ // the previous permissive behavior and skip validation.
160
+ if (this.#roots === undefined && this.#allowUnrestrictedPaths) {
161
+ // Canonical path might not exist yet so we fallback to
162
+ // path.resolve(filePath). Consumers should not follow symlinks.
163
+ return canonicalPath || path.resolve(filePath);
164
+ }
165
+ // roots() always returns at least the temp directory, even if the
166
+ // connecting client never negotiated the optional `roots` capability.
167
+ // Path validation must not be skipped just because no workspace roots
168
+ // were configured.
169
+ const roots = this.roots();
170
+ let allowed = false;
171
+ const resolvedRoots = await Promise.allSettled(roots.map(async (root) => {
172
+ const rootPathUri = root.uri;
173
+ const rootPath = path.resolve(fileURLToPath(rootPathUri));
174
+ return await fs.realpath(rootPath);
175
+ }));
176
+ for (let i = 0; i < roots.length; i++) {
177
+ const root = roots[i];
178
+ const result = resolvedRoots[i];
179
+ if (result.status === 'fulfilled') {
180
+ const canonicalRoot = result.value;
181
+ if (canonicalPath === canonicalRoot ||
182
+ canonicalPath.startsWith(canonicalRoot + path.sep)) {
183
+ allowed = true;
184
+ break;
185
+ }
186
+ }
187
+ else {
188
+ const rootErr = result.reason;
189
+ const errMsg = rootErr instanceof Error ? rootErr.message : String(rootErr);
190
+ console.warn(`[MCP Context] Could not resolve configured root ${root.uri}: ${errMsg}`);
191
+ // Skip this root if it cannot be resolved.
192
+ }
193
+ }
194
+ if (!allowed) {
195
+ throw new Error(`Access denied: path ${filePath} (canonical: ${canonicalPath}) is not within any of the configured workspace roots.`);
196
+ }
197
+ return canonicalPath || path.resolve(filePath);
198
+ }
199
+ async ensureExtension(filePath, extension) {
200
+ const resolved = await this.validatePath(filePath);
201
+ const currentExtension = path.extname(resolved);
202
+ const outputPath = `${resolved.slice(0, resolved.length - currentExtension.length)}${extension}`;
203
+ return outputPath;
204
+ }
205
+ async newPage(background, isolatedContextName) {
206
+ let page;
207
+ if (isolatedContextName !== undefined) {
208
+ let ctx = this.#isolatedContexts.get(isolatedContextName);
209
+ if (!ctx) {
210
+ ctx = await this.browser.createBrowserContext();
211
+ this.#isolatedContexts.set(isolatedContextName, ctx);
212
+ }
213
+ page = await ctx.newPage({ background });
214
+ }
215
+ else {
216
+ page = await this.browser.newPage({ background });
217
+ }
218
+ const mcpPage = await this.#createMcpPage(page);
219
+ await this.createPagesSnapshot();
220
+ this.selectPage(mcpPage);
221
+ return mcpPage;
222
+ }
223
+ async closePage(pageId) {
224
+ if (this.#mcpPages.size === 1) {
225
+ throw new Error(CLOSE_PAGE_ERROR);
226
+ }
227
+ const page = this.getPageById(pageId);
228
+ if (page) {
229
+ page.dispose();
230
+ this.#mcpPages.delete(page.pptrPage);
231
+ }
232
+ await page.pptrPage.close({ runBeforeUnload: false });
233
+ }
234
+ get #hasNetworkBlockOrAllowlist() {
235
+ return !!(this.#options.allowlist || this.#options.blocklist);
236
+ }
237
+ installPWA(options) {
238
+ return this.browser.installPWA(options);
239
+ }
240
+ uninstallPWA(options) {
241
+ return this.browser.uninstallPWA(options);
242
+ }
243
+ launchPWA(options) {
244
+ return this.browser.launchPWA(options);
245
+ }
246
+ getPWAState(options) {
247
+ return this.browser.getPWAState(options);
248
+ }
249
+ setIsRunningPerformanceTrace(x) {
250
+ this.#isRunningTrace = x;
251
+ }
252
+ isRunningPerformanceTrace() {
253
+ return this.#isRunningTrace;
254
+ }
255
+ getScreenRecorder() {
256
+ return this.#screenRecorderData;
257
+ }
258
+ setScreenRecorder(data) {
259
+ this.#screenRecorderData = data;
260
+ }
261
+ isCruxEnabled() {
262
+ return this.#options.performanceCrux;
263
+ }
264
+ getPages() {
265
+ return Array.from(this.#mcpPages.values());
266
+ }
267
+ getSelectedMcpPage() {
268
+ const page = this.#selectedPage;
269
+ if (!page) {
270
+ throw new Error('No page selected');
271
+ }
272
+ if (page.pptrPage.isClosed()) {
273
+ throw new Error('The selected page has been closed. Call list_pages to see open pages.');
274
+ }
275
+ return page;
276
+ }
277
+ getSelectedMcpPageUrl(page) {
278
+ let targetPage = page;
279
+ if (!targetPage) {
280
+ try {
281
+ targetPage = this.getSelectedMcpPage();
282
+ }
283
+ catch {
284
+ return undefined;
285
+ }
286
+ }
287
+ if (targetPage?.pptrPage?.isClosed() === false) {
288
+ return targetPage.pptrPage.url();
289
+ }
290
+ return undefined;
291
+ }
292
+ async getDevToolsData(page) {
293
+ const targetPage = page ?? this.#selectedPage;
294
+ if (!targetPage) {
295
+ return undefined;
296
+ }
297
+ let timeoutId;
298
+ const timeoutPromise = new Promise(resolve => {
299
+ timeoutId = setTimeout(() => resolve(undefined), 500);
300
+ });
301
+ const dataPromise = targetPage.getDevToolsData();
302
+ try {
303
+ return await Promise.race([dataPromise, timeoutPromise]);
304
+ }
305
+ catch {
306
+ return undefined;
307
+ }
308
+ finally {
309
+ clearTimeout(timeoutId);
310
+ }
311
+ }
312
+ /**
313
+ * Returns true once if this context was created by reconnecting after the
314
+ * previous browser connection was lost, so the next response can surface a
315
+ * note. Cleared on first call.
316
+ */
317
+ consumeReconnectNotice() {
318
+ const notice = this.#reconnectNotice;
319
+ this.#reconnectNotice = false;
320
+ return notice;
321
+ }
322
+ getPageById(pageId) {
323
+ const page = this.#mcpPages.values().find(mcpPage => mcpPage.id === pageId);
324
+ if (!page) {
325
+ throw new Error('No page found');
326
+ }
327
+ return page;
328
+ }
329
+ isPageSelected(page) {
330
+ return this.#selectedPage === page;
331
+ }
332
+ selectPage(newPage) {
333
+ this.#selectedPage = newPage;
334
+ newPage.updateTimeouts();
335
+ }
336
+ /**
337
+ * Returns details about the last page snapshot automatically replacing the
338
+ * selection because the selected page disappeared from the page list, or
339
+ * `undefined` if the snapshot left the selection intact. Recomputed on every
340
+ * createPagesSnapshot() call.
341
+ */
342
+ getSelectedPageFallback() {
343
+ return this.#selectedPageFallback;
344
+ }
345
+ /**
346
+ * Creates a snapshot of the extension service workers.
347
+ */
348
+ async createExtensionServiceWorkersSnapshot() {
349
+ const allTargets = this.browser.targets();
350
+ const serviceWorkers = allTargets.filter(target => {
351
+ return (target.type() === 'service_worker' &&
352
+ target.url().includes('chrome-extension://'));
353
+ });
354
+ for (const serviceWorker of serviceWorkers) {
355
+ if (!this.#extensionServiceWorkerMap.has(serviceWorker)) {
356
+ this.#extensionServiceWorkerMap.set(serviceWorker, 'sw-' + this.#nextExtensionServiceWorkerId++);
357
+ }
358
+ }
359
+ this.#extensionServiceWorkers = serviceWorkers.map(serviceWorker => {
360
+ return {
361
+ target: serviceWorker,
362
+ id: this.#extensionServiceWorkerMap.get(serviceWorker),
363
+ url: serviceWorker.url(),
364
+ };
365
+ });
366
+ return this.#extensionServiceWorkers;
367
+ }
368
+ getServiceWorkerConsoleData(extensionId) {
369
+ return this.#serviceWorkerConsoleCollector.getData(extensionId);
370
+ }
371
+ #getBrowserContextToNameMap() {
372
+ // Build a reverse lookup from BrowserContext instance → name.
373
+ const contextToName = new Map();
374
+ for (const [name, ctx] of this.#isolatedContexts) {
375
+ contextToName.set(ctx, name);
376
+ }
377
+ const defaultCtx = this.browser.defaultBrowserContext();
378
+ // Auto-discover BrowserContexts not in our mapping (e.g., externally
379
+ // created incognito contexts) and assign generated names.
380
+ const knownContexts = new Set(this.#isolatedContexts.values());
381
+ for (const ctx of this.browser.browserContexts()) {
382
+ if (ctx !== defaultCtx && !ctx.closed && !knownContexts.has(ctx)) {
383
+ const name = `isolated-context-${this.#nextIsolatedContextId++}`;
384
+ this.#isolatedContexts.set(name, ctx);
385
+ contextToName.set(ctx, name);
386
+ }
387
+ }
388
+ return contextToName;
389
+ }
390
+ async #createMcpPage(page) {
391
+ let mcpPage = this.#mcpPages.get(page);
392
+ if (!mcpPage) {
393
+ mcpPage = new McpPage(page, nextPageId++, {
394
+ locatorClass: this.#locatorClass,
395
+ hasNetworkBlockOrAllowlist: this.#hasNetworkBlockOrAllowlist,
396
+ isolatedContextName: this.#getBrowserContextToNameMap().get(page.browserContext()),
397
+ navigationTimeout: this.#options.navigationTimeout,
398
+ sourceMaps: this.#options.sourceMaps,
399
+ onNotification: this.#options.onNotification,
400
+ });
401
+ this.#mcpPages.set(page, mcpPage);
402
+ await mcpPage.init();
403
+ }
404
+ return mcpPage;
405
+ }
406
+ async createPagesSnapshot() {
407
+ const allPages = await this.#fetchBrowserPages();
408
+ await Promise.allSettled(allPages.map(page => this.#createMcpPage(page)));
409
+ // Prune orphaned #mcpPages entries (pages that no longer exist).
410
+ const currentPages = new Set(allPages);
411
+ for (const [page, mcpPage] of this.#mcpPages) {
412
+ if (!currentPages.has(page)) {
413
+ mcpPage.dispose();
414
+ this.#mcpPages.delete(page);
415
+ }
416
+ }
417
+ const pages = Array.from(this.#mcpPages.values());
418
+ // Only fall back when the selected page is actually gone. Gating on
419
+ // `isClosed()` instead of `pages` membership avoids silently swapping a
420
+ // live page that is momentarily missing from the snapshot.
421
+ this.#selectedPageFallback = undefined;
422
+ if ((!this.#selectedPage || this.#selectedPage.pptrPage.isClosed()) &&
423
+ pages[0]) {
424
+ // Record the automatic change so the response can surface it. Skipped on
425
+ // first connect, when there was no prior selection to replace.
426
+ if (this.#selectedPage) {
427
+ this.#selectedPageFallback = {
428
+ wasClosed: this.#selectedPage.pptrPage.isClosed(),
429
+ };
430
+ }
431
+ this.selectPage(pages[0]);
432
+ }
433
+ return pages.map(p => p.pptrPage);
434
+ }
435
+ async #fetchBrowserPages() {
436
+ const allPages = (await this.browser.pages(this.#options.experimentalIncludeAllPages)).filter(page => {
437
+ if (!this.#options.experimentalDevToolsDebugging &&
438
+ page.url().startsWith('devtools://')) {
439
+ return false;
440
+ }
441
+ return isAllowedUrl(page.url(), {
442
+ categoryExtensions: this.#options.categoryExtensions,
443
+ });
444
+ });
445
+ if (this.#options.categoryExtensions) {
446
+ const allTargets = this.browser.targets();
447
+ const extensionTargets = allTargets.filter(target => {
448
+ return (target.url().startsWith('chrome-extension://') &&
449
+ target.type() === 'page');
450
+ });
451
+ await Promise.allSettled(extensionTargets.map(async (target) => {
452
+ try {
453
+ let page = await target.page();
454
+ if (!page) {
455
+ page = await target.asPage();
456
+ }
457
+ this.#extensionPages.set(target, page);
458
+ if (page &&
459
+ isAllowedUrl(page.url(), { categoryExtensions: true }) &&
460
+ !allPages.includes(page)) {
461
+ allPages.push(page);
462
+ }
463
+ }
464
+ catch (e) {
465
+ this.logger?.('Failed to get page for extension target', e);
466
+ }
467
+ }));
468
+ }
469
+ return allPages;
470
+ }
471
+ getExtensionServiceWorkers() {
472
+ return this.#extensionServiceWorkers;
473
+ }
474
+ getExtensionServiceWorkerId(extensionServiceWorker) {
475
+ return this.#extensionServiceWorkerMap.get(extensionServiceWorker.target);
476
+ }
477
+ async #writeFile(filepath, data) {
478
+ const resolved = await this.validatePath(filepath);
479
+ try {
480
+ await fs.mkdir(path.dirname(resolved), { recursive: true });
481
+ // Open the file with flags to:
482
+ // - O_WRONLY: Write-only
483
+ // - O_CREAT: Create if it doesn't exist
484
+ // - O_TRUNC: Truncate to zero length if it exists
485
+ // - O_NOFOLLOW: DO NOT follow symlinks.
486
+ // - 0o600: Permissions: read/write for owner, no permissions for others.
487
+ await fs.writeFile(resolved, data, {
488
+ flag: fs.constants.O_WRONLY |
489
+ fs.constants.O_CREAT |
490
+ fs.constants.O_TRUNC |
491
+ fs.constants.O_NOFOLLOW,
492
+ mode: 0o600,
493
+ });
494
+ }
495
+ catch (err) {
496
+ throw new Error(`Could not write ${filepath}`, { cause: err });
497
+ }
498
+ }
499
+ async saveTemporaryFile(data, filename) {
500
+ const filepath = await getTempFilePath(filename);
501
+ await this.#writeFile(filepath, data);
502
+ return { filepath };
503
+ }
504
+ async saveFile(data, clientProvidedFilePath, extension) {
505
+ const filePath = await this.ensureExtension(clientProvidedFilePath, extension);
506
+ await this.#writeFile(filePath, data);
507
+ return { filename: filePath };
508
+ }
509
+ storeTraceRecording(result) {
510
+ // Clear the trace results because we only consume the latest trace currently.
511
+ this.#traceResults = [];
512
+ this.#traceResults.push(result);
513
+ }
514
+ recordedTraces() {
515
+ return this.#traceResults;
516
+ }
517
+ async installExtension(extensionPath) {
518
+ const id = await Promise.race([
519
+ this.browser.installExtension(extensionPath),
520
+ new Promise((_, rej) => setTimeout(() => rej(new Error('Timeout installing extension')), 30000)),
521
+ ]);
522
+ return id;
523
+ }
524
+ async uninstallExtension(id) {
525
+ await Promise.race([
526
+ this.browser.uninstallExtension(id),
527
+ new Promise((_, rej) => setTimeout(() => rej(new Error('Timeout uninstalling extension')), 30000)),
528
+ ]);
529
+ }
530
+ async triggerExtensionAction(id) {
531
+ const extensions = await this.browser.extensions();
532
+ const extension = extensions.get(id);
533
+ if (!extension) {
534
+ throw new Error(`Extension with ID ${id} not found.`);
535
+ }
536
+ const page = this.getSelectedMcpPage().pptrPage;
537
+ await extension.triggerAction(page);
538
+ }
539
+ listExtensions() {
540
+ return this.browser.extensions();
541
+ }
542
+ async getExtension(id) {
543
+ const pptrExtensions = await this.browser.extensions();
544
+ return pptrExtensions.get(id);
545
+ }
546
+ async getHeapSnapshotAggregates(filePath, filterName, objectId) {
547
+ return await this.#heapSnapshotManager.getAggregates(filePath, filterName, objectId);
548
+ }
549
+ async getHeapSnapshotDuplicateStrings(filePath) {
550
+ return await this.#heapSnapshotManager.getDuplicateStrings(filePath);
551
+ }
552
+ async queryHeapSnapshotObjects(filePath, options) {
553
+ return await this.#heapSnapshotManager.queryObjects(filePath, options);
554
+ }
555
+ async getHeapSnapshotStats(filePath) {
556
+ return await this.#heapSnapshotManager.getStats(filePath);
557
+ }
558
+ async getHeapSnapshotStaticData(filePath) {
559
+ return await this.#heapSnapshotManager.getStaticData(filePath);
560
+ }
561
+ async getHeapSnapshotNativeContextSizes(filePath) {
562
+ return await this.#heapSnapshotManager.getNativeContextSizes(filePath);
563
+ }
564
+ async getHeapSnapshotRetainedByContextSummary(filePath) {
565
+ return await this.#heapSnapshotManager.getRetainedByContextSummary(filePath);
566
+ }
567
+ async getHeapSnapshotNodesById(filePath, id, filterName, objectId) {
568
+ return await this.#heapSnapshotManager.getNodesById(filePath, id, filterName, objectId);
569
+ }
570
+ async getHeapSnapshotRetainers(filePath, nodeId) {
571
+ return await this.#heapSnapshotManager.getRetainers(filePath, nodeId);
572
+ }
573
+ async getHeapSnapshotObjectDetails(filePath, nodeId) {
574
+ return await this.#heapSnapshotManager.getObjectInfo(filePath, nodeId);
575
+ }
576
+ async closeHeapSnapshot(filePath) {
577
+ return this.#heapSnapshotManager.disposeSnapshot(filePath);
578
+ }
579
+ hasHeapSnapshots() {
580
+ return this.#heapSnapshotManager.hasSnapshots();
581
+ }
582
+ async getHeapSnapshotRetainingPaths(filePath, nodeId, maxDepth, maxNodes, maxSiblings) {
583
+ return await this.#heapSnapshotManager.getRetainingPaths(filePath, nodeId, maxDepth, maxNodes, maxSiblings);
584
+ }
585
+ async getHeapSnapshotDominators(filePath, nodeId) {
586
+ return await this.#heapSnapshotManager.getDominatorsOf(filePath, nodeId);
587
+ }
588
+ #validateUrlNotBlocked(url) {
589
+ if (!this.#options.blocklist) {
590
+ return;
591
+ }
592
+ for (const block of this.#options.blocklist) {
593
+ const pattern = new URLPattern(block);
594
+ if (pattern.test(url)) {
595
+ throw new Error(`Blocked by blocklist: ${url}`);
596
+ }
597
+ }
598
+ }
599
+ #validateUrlAllowed(url) {
600
+ if (!this.#options.allowlist) {
601
+ return;
602
+ }
603
+ for (const allow of this.#options.allowlist) {
604
+ const pattern = new URLPattern(allow);
605
+ if (pattern.test(url)) {
606
+ return;
607
+ }
608
+ }
609
+ throw new Error(`Not allowed by allowlist: ${url}`);
610
+ }
611
+ async loadResource(path) {
612
+ const url = new URL(path);
613
+ this.#validateUrlNotBlocked(url);
614
+ switch (url.protocol) {
615
+ case 'https:':
616
+ case 'http:': {
617
+ this.#validateUrlAllowed(url);
618
+ const response = await fetch(url);
619
+ if (!response.ok) {
620
+ throw new Error(`Failed to load resource: ${url}`);
621
+ }
622
+ return response.text();
623
+ }
624
+ case 'file:': {
625
+ const resolved = await this.validatePath(fileURLToPath(url));
626
+ return await fs.readFile(resolved, 'utf-8');
627
+ }
628
+ default:
629
+ throw new Error(`Unsupported protocol for: ${url}`);
630
+ }
631
+ }
632
+ async getHeapSnapshotEdges(filePath, nodeId, options) {
633
+ return await this.#heapSnapshotManager.getEdges(filePath, nodeId, options);
634
+ }
635
+ async getHeapSnapshotClassDiffs(baseFilePath, currentFilePath) {
636
+ return await this.#heapSnapshotManager.getClassDiffs(baseFilePath, currentFilePath);
637
+ }
638
+ async getHeapSnapshotDetailedClassDiff(baseFilePath, currentFilePath, classIndex) {
639
+ return await this.#heapSnapshotManager.getDetailedClassDiff(baseFilePath, currentFilePath, classIndex);
640
+ }
641
+ }
642
+ //# sourceMappingURL=McpContext.js.map