@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,189 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Google LLC
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ process.title = 'chrome-devtools';
8
+ import process from 'node:process';
9
+ import { startDaemon, stopDaemon, sendCommand, handleResponse, } from '../daemon/client.js';
10
+ import { isDaemonRunning, serializeArgs } from '../daemon/utils.js';
11
+ import { logDisclaimers } from '../index.js';
12
+ import { hideBin, yargs } from '../third_party/index.js';
13
+ import { checkForUpdates } from '../utils/check-for-updates.js';
14
+ import { VERSION } from '../version.js';
15
+ import { commands } from './chrome-devtools-cli-options.js';
16
+ import { cliOptions, parseArguments } from './chrome-devtools-mcp-cli-options.js';
17
+ await checkForUpdates('Run `npm install -g @gabrielbryk/arc-devtools-mcp@latest` and `arc-devtools start` to update and restart the daemon.');
18
+ async function start(args, sessionId) {
19
+ const combinedArgs = [...args, ...defaultArgs];
20
+ await startDaemon(combinedArgs, sessionId);
21
+ logDisclaimers(parseArguments(VERSION, combinedArgs));
22
+ }
23
+ const defaultArgs = ['--viaCli', '--experimentalStructuredContent'];
24
+ const startCliOptions = {
25
+ ...cliOptions,
26
+ };
27
+ // Missing CLI serialization.
28
+ delete startCliOptions.viewport;
29
+ // Change the defaults for the CLI.
30
+ delete startCliOptions.experimentalStructuredContent;
31
+ delete startCliOptions.experimentalInteropTools;
32
+ delete startCliOptions.experimentalPageIdRouting;
33
+ if (!('default' in cliOptions.headless)) {
34
+ throw new Error('headless cli option unexpectedly does not have a default');
35
+ }
36
+ if ('default' in cliOptions.isolated) {
37
+ throw new Error('isolated cli option unexpectedly has a default');
38
+ }
39
+ startCliOptions.headless.default = true;
40
+ startCliOptions.isolated.description =
41
+ 'If specified, creates a temporary user-data-dir that is automatically cleaned up after the browser is closed. Defaults to true unless userDataDir is provided.';
42
+ startCliOptions.categoryExtensions.default = true;
43
+ const y = yargs(hideBin(process.argv))
44
+ .scriptName('arc-devtools')
45
+ .showHelpOnFail(true)
46
+ .usage('arc-devtools <command> [...args] --flags')
47
+ .usage(`Run 'arc-devtools <command> --help' for help on the specific command.`)
48
+ .option('sessionId', {
49
+ type: 'string',
50
+ description: 'Session ID for daemon scoping',
51
+ default: '',
52
+ hidden: true,
53
+ })
54
+ .demandCommand()
55
+ .version(VERSION)
56
+ .strict()
57
+ .help(true)
58
+ .wrap(120);
59
+ y.command('start', 'Start or restart chrome-devtools-mcp', y => y
60
+ .options(startCliOptions)
61
+ .example('$0 start --browserUrl http://localhost:9222', 'Start the server connecting to an existing browser')
62
+ .strict(), async (argv) => {
63
+ if (isDaemonRunning(argv.sessionId)) {
64
+ await stopDaemon(argv.sessionId);
65
+ }
66
+ // Defaults but we do not want to affect the yargs conflict resolution.
67
+ if (argv.isolated === undefined && argv.userDataDir === undefined) {
68
+ argv.isolated = true;
69
+ }
70
+ if (argv.headless === undefined) {
71
+ argv.headless = true;
72
+ }
73
+ const args = serializeArgs(cliOptions, argv);
74
+ await start(args, argv.sessionId);
75
+ process.exit(0);
76
+ }).strict(); // Re-enable strict validation for other commands; this is applied to the yargs instance itself
77
+ y.command('status', 'Checks if chrome-devtools-mcp is running', y => y, async (argv) => {
78
+ if (isDaemonRunning(argv.sessionId)) {
79
+ console.log('chrome-devtools-mcp daemon is running.');
80
+ const response = await sendCommand({
81
+ method: 'status',
82
+ }, argv.sessionId);
83
+ if (response.success) {
84
+ const data = JSON.parse(response.result);
85
+ console.log(`pid=${data.pid} socket=${data.socketPath} start-date=${data.startDate} version=${data.version}`);
86
+ console.log(`args=${JSON.stringify(data.args)}`);
87
+ }
88
+ else {
89
+ console.error('Error:', response.error);
90
+ process.exit(1);
91
+ }
92
+ }
93
+ else {
94
+ console.log('chrome-devtools-mcp daemon is not running.');
95
+ }
96
+ process.exit(0);
97
+ });
98
+ y.command('stop', 'Stop chrome-devtools-mcp if any', y => y, async (argv) => {
99
+ const sessionId = argv.sessionId;
100
+ if (!isDaemonRunning(sessionId)) {
101
+ process.exit(0);
102
+ }
103
+ await stopDaemon(sessionId);
104
+ process.exit(0);
105
+ });
106
+ for (const [commandName, commandDef] of Object.entries(commands)) {
107
+ const args = commandDef.args;
108
+ const requiredArgNames = Object.keys(args).filter(name => args[name].required);
109
+ const optionalArgNames = Object.keys(args).filter(name => !args[name].required);
110
+ let commandStr = commandName;
111
+ for (const arg of requiredArgNames) {
112
+ commandStr += ` <${arg}>`;
113
+ }
114
+ for (const arg of optionalArgNames) {
115
+ commandStr += ` [--${arg}]`;
116
+ }
117
+ y.command(commandStr, commandDef.description, y => {
118
+ y.option('output-format', {
119
+ choices: ['md', 'json'],
120
+ default: 'md',
121
+ });
122
+ for (const [argName, opt] of Object.entries(args)) {
123
+ const type = opt.type === 'integer' || opt.type === 'number'
124
+ ? 'number'
125
+ : opt.type === 'boolean'
126
+ ? 'boolean'
127
+ : opt.type === 'array'
128
+ ? 'array'
129
+ : 'string';
130
+ if (opt.required) {
131
+ const options = {
132
+ describe: opt.description,
133
+ type: type,
134
+ };
135
+ if (opt.default !== undefined) {
136
+ options.default = opt.default;
137
+ }
138
+ if (opt.enum) {
139
+ options.choices = opt.enum;
140
+ }
141
+ y.positional(argName, options);
142
+ }
143
+ else {
144
+ const options = {
145
+ describe: opt.description,
146
+ type: type,
147
+ };
148
+ if (opt.default !== undefined) {
149
+ options.default = opt.default;
150
+ }
151
+ if (opt.enum) {
152
+ options.choices = opt.enum;
153
+ }
154
+ y.option(argName, options);
155
+ }
156
+ }
157
+ }, async (argv) => {
158
+ const sessionId = argv.sessionId;
159
+ try {
160
+ if (!isDaemonRunning(sessionId)) {
161
+ await start([], sessionId);
162
+ }
163
+ const commandArgs = {};
164
+ for (const argName of Object.keys(args)) {
165
+ if (argName in argv) {
166
+ commandArgs[argName] = argv[argName];
167
+ }
168
+ }
169
+ const response = await sendCommand({
170
+ method: 'invoke_tool',
171
+ tool: commandName,
172
+ args: commandArgs,
173
+ }, sessionId);
174
+ if (response.success) {
175
+ console.log(await handleResponse(JSON.parse(response.result), argv['output-format']));
176
+ }
177
+ else {
178
+ console.error('Error:', response.error);
179
+ process.exit(1);
180
+ }
181
+ }
182
+ catch (error) {
183
+ console.error('Failed to execute command:', error);
184
+ process.exit(1);
185
+ }
186
+ });
187
+ }
188
+ await y.parse();
189
+ //# sourceMappingURL=chrome-devtools.js.map
@@ -0,0 +1,253 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { execSync } from 'node:child_process';
7
+ import fs from 'node:fs';
8
+ import os from 'node:os';
9
+ import path from 'node:path';
10
+ import { logger } from './logger.js';
11
+ import { puppeteer } from './third_party/index.js';
12
+ let browser;
13
+ let browserMode;
14
+ function makeTargetFilter(enableExtensions = false) {
15
+ // `arc://` is the Arc browser's internal scheme (analogous to `chrome://`).
16
+ // Filtering it keeps Arc's internal pages out of the page list. These entries
17
+ // are harmless on Chrome, where no `arc://` targets exist.
18
+ const ignoredPrefixes = new Set([
19
+ 'chrome://',
20
+ 'chrome-untrusted://',
21
+ 'arc://',
22
+ ]);
23
+ if (!enableExtensions) {
24
+ ignoredPrefixes.add('chrome-extension://');
25
+ }
26
+ return function targetFilter(target) {
27
+ // Keep new-tab pages visible so they can be reused (see McpContext.newPage
28
+ // under `--arc`). Arc's new tab may surface as `arc://newtab` or `about:blank`.
29
+ if (target.url() === 'chrome://newtab/' ||
30
+ target.url().startsWith('arc://newtab')) {
31
+ return true;
32
+ }
33
+ // Could be the only page opened in the browser.
34
+ if (target.url().startsWith('chrome://inspect') ||
35
+ target.url().startsWith('arc://inspect')) {
36
+ return true;
37
+ }
38
+ for (const prefix of ignoredPrefixes) {
39
+ if (target.url().startsWith(prefix)) {
40
+ return false;
41
+ }
42
+ }
43
+ return true;
44
+ };
45
+ }
46
+ export async function ensureBrowserConnected(options) {
47
+ const { channel, enableExtensions } = options;
48
+ if (browser?.connected) {
49
+ return browser;
50
+ }
51
+ const connectOptions = {
52
+ targetFilter: makeTargetFilter(enableExtensions),
53
+ defaultViewport: null,
54
+ handleDevToolsAsPage: true,
55
+ blocklist: options.blocklist,
56
+ allowlist: options.allowlist,
57
+ };
58
+ let autoConnect = false;
59
+ if (options.wsEndpoint) {
60
+ connectOptions.browserWSEndpoint = options.wsEndpoint;
61
+ if (options.wsHeaders) {
62
+ connectOptions.headers = options.wsHeaders;
63
+ }
64
+ }
65
+ else if (options.browserURL) {
66
+ connectOptions.browserURL = options.browserURL;
67
+ }
68
+ else if (channel || options.userDataDir) {
69
+ const userDataDir = options.userDataDir;
70
+ if (userDataDir) {
71
+ autoConnect = true;
72
+ // TODO: re-expose this logic via Puppeteer.
73
+ const portPath = path.join(userDataDir, 'DevToolsActivePort');
74
+ try {
75
+ const fileContent = await fs.promises.readFile(portPath, 'utf8');
76
+ const [rawPort, rawPath] = fileContent
77
+ .split('\n')
78
+ .map(line => {
79
+ return line.trim();
80
+ })
81
+ .filter(line => {
82
+ return !!line;
83
+ });
84
+ if (!rawPort || !rawPath) {
85
+ throw new Error(`Invalid DevToolsActivePort '${fileContent}' found`);
86
+ }
87
+ const port = parseInt(rawPort, 10);
88
+ if (isNaN(port) || port <= 0 || port > 65535) {
89
+ throw new Error(`Invalid port '${rawPort}' found`);
90
+ }
91
+ const browserWSEndpoint = `ws://127.0.0.1:${port}${rawPath}`;
92
+ connectOptions.browserWSEndpoint = browserWSEndpoint;
93
+ }
94
+ catch (error) {
95
+ throw new Error(`Could not connect to Chrome/Arc in ${userDataDir}. Check if the browser is running and remote debugging is enabled by going to chrome://inspect/#remote-debugging (or arc://inspect/#remote-debugging in Arc).`, {
96
+ cause: error,
97
+ });
98
+ }
99
+ }
100
+ else {
101
+ if (!channel) {
102
+ throw new Error('Channel must be provided if userDataDir is missing');
103
+ }
104
+ connectOptions.channel = (channel === 'stable' ? 'chrome' : `chrome-${channel}`);
105
+ }
106
+ }
107
+ else {
108
+ throw new Error('Either browserURL, wsEndpoint, channel or userDataDir must be provided');
109
+ }
110
+ logger?.('Connecting Puppeteer to ', JSON.stringify(connectOptions));
111
+ try {
112
+ // Assign mode before browser so a concurrent closeBrowser() never sees
113
+ // `browser` set with `browserMode` still undefined (would fall through
114
+ // to the disconnect() path and orphan a launched Chrome).
115
+ const connected = await puppeteer.connect(connectOptions);
116
+ browserMode = 'connected';
117
+ browser = connected;
118
+ }
119
+ catch (err) {
120
+ throw new Error(`Could not connect to Chrome/Arc. ${autoConnect ? `Check if the browser is running and remote debugging is enabled by going to chrome://inspect/#remote-debugging (or arc://inspect/#remote-debugging in Arc).` : `Check if the browser is running.`}`, {
121
+ cause: err,
122
+ });
123
+ }
124
+ logger?.('Connected Puppeteer');
125
+ return browser;
126
+ }
127
+ export function detectDisplay() {
128
+ // Only detect display on Linux/UNIX.
129
+ if (os.platform() === 'win32' || os.platform() === 'darwin') {
130
+ return;
131
+ }
132
+ if (!process.env['DISPLAY']) {
133
+ try {
134
+ const result = execSync(`ps -u $(id -u) -o pid= | xargs -I{} cat /proc/{}/environ 2>/dev/null | tr '\\0' '\\n' | grep -m1 '^DISPLAY=' | cut -d= -f2`);
135
+ const display = result.toString('utf8').trim();
136
+ process.env['DISPLAY'] = display;
137
+ }
138
+ catch {
139
+ // no-op
140
+ }
141
+ }
142
+ }
143
+ export async function launch(options) {
144
+ const { channel, executablePath, headless, isolated } = options;
145
+ const profileDirName = channel && channel !== 'stable'
146
+ ? `chrome-profile-${channel}`
147
+ : 'chrome-profile';
148
+ let userDataDir = options.userDataDir;
149
+ if (!isolated && !userDataDir) {
150
+ userDataDir = path.join(os.homedir(), '.cache', options.viaCli ? 'chrome-devtools-mcp-cli' : 'chrome-devtools-mcp', profileDirName);
151
+ await fs.promises.mkdir(userDataDir, {
152
+ recursive: true,
153
+ });
154
+ }
155
+ const args = [
156
+ ...(options.chromeArgs ?? []),
157
+ '--hide-crash-restore-bubble',
158
+ ];
159
+ const ignoreDefaultArgs = options.ignoreDefaultChromeArgs ?? false;
160
+ if (headless) {
161
+ args.push('--screen-info={3840x2160}');
162
+ }
163
+ let puppeteerChannel;
164
+ if (options.devtools) {
165
+ args.push('--auto-open-devtools-for-tabs');
166
+ }
167
+ if (!executablePath) {
168
+ puppeteerChannel =
169
+ channel && channel !== 'stable'
170
+ ? `chrome-${channel}`
171
+ : 'chrome';
172
+ }
173
+ if (!headless) {
174
+ detectDisplay();
175
+ }
176
+ try {
177
+ const browser = await puppeteer.launch({
178
+ channel: puppeteerChannel,
179
+ targetFilter: makeTargetFilter(options.enableExtensions),
180
+ executablePath,
181
+ defaultViewport: null,
182
+ userDataDir,
183
+ pipe: true,
184
+ headless,
185
+ args,
186
+ ignoreDefaultArgs: ignoreDefaultArgs,
187
+ acceptInsecureCerts: options.acceptInsecureCerts,
188
+ handleDevToolsAsPage: true,
189
+ enableExtensions: options.enableExtensions,
190
+ blocklist: options.blocklist,
191
+ allowlist: options.allowlist,
192
+ });
193
+ if (options.logFile) {
194
+ // FIXME: we are probably subscribing too late to catch startup logs. We
195
+ // should expose the process earlier or expose the getRecentLogs() getter.
196
+ browser.process()?.stderr?.pipe(options.logFile);
197
+ browser.process()?.stdout?.pipe(options.logFile);
198
+ }
199
+ if (options.viewport) {
200
+ const [page] = await browser.pages();
201
+ await page?.resize({
202
+ contentWidth: options.viewport.width,
203
+ contentHeight: options.viewport.height,
204
+ });
205
+ }
206
+ return browser;
207
+ }
208
+ catch (error) {
209
+ if (userDataDir &&
210
+ error.message.includes('The browser is already running')) {
211
+ throw new Error(`The browser is already running for ${userDataDir}. Use --isolated to run multiple browser instances.`, {
212
+ cause: error,
213
+ });
214
+ }
215
+ throw error;
216
+ }
217
+ }
218
+ export async function ensureBrowserLaunched(options) {
219
+ if (browser?.connected) {
220
+ return browser;
221
+ }
222
+ // Assign mode before browser; see the connect path above for rationale.
223
+ const launched = await launch(options);
224
+ browserMode = 'launched';
225
+ browser = launched;
226
+ return browser;
227
+ }
228
+ /**
229
+ * Shutdown hook for the active browser. Closes a launched browser (so the
230
+ * Chrome subprocess is reaped) or disconnects from an attached browser (so
231
+ * the user's Chrome instance stays alive). No-op if no browser is active or
232
+ * the connection has already been dropped. Called from the server entrypoint
233
+ * on stdin EOF / SIGTERM / SIGINT.
234
+ */
235
+ export async function closeBrowser() {
236
+ const b = browser;
237
+ const mode = browserMode;
238
+ browser = undefined;
239
+ browserMode = undefined;
240
+ if (!b || !b.connected) {
241
+ return;
242
+ }
243
+ if (mode === 'launched') {
244
+ await b.close().catch(err => {
245
+ logger?.('Failed to close browser', err);
246
+ });
247
+ return;
248
+ }
249
+ await b.disconnect().catch(err => {
250
+ logger?.('Failed to disconnect from browser', err);
251
+ });
252
+ }
253
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1,160 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { spawn } from 'node:child_process';
7
+ import fs from 'node:fs';
8
+ import net from 'node:net';
9
+ import { logger } from '../logger.js';
10
+ import { PipeTransport } from '../third_party/index.js';
11
+ import { getTempFilePath } from '../utils/files.js';
12
+ import { DAEMON_SCRIPT_PATH, getSocketPath, getPidFilePath, isDaemonRunning, } from './utils.js';
13
+ const FILE_TIMEOUT = 10_000;
14
+ /**
15
+ * Waits for a file to be created and populated (removed = false) or removed (removed = true).
16
+ */
17
+ function waitForFile(filePath, removed = false) {
18
+ return new Promise((resolve, reject) => {
19
+ const check = () => {
20
+ const exists = fs.existsSync(filePath);
21
+ if (removed) {
22
+ return !exists;
23
+ }
24
+ if (!exists) {
25
+ return false;
26
+ }
27
+ try {
28
+ return fs.statSync(filePath).size > 0;
29
+ }
30
+ catch {
31
+ return false;
32
+ }
33
+ };
34
+ if (check()) {
35
+ resolve();
36
+ return;
37
+ }
38
+ const timer = setTimeout(() => {
39
+ fs.unwatchFile(filePath);
40
+ reject(new Error(`Timeout: file ${filePath} ${removed ? 'not removed' : 'not found'} within ${FILE_TIMEOUT}ms`));
41
+ }, FILE_TIMEOUT);
42
+ fs.watchFile(filePath, { interval: 500 }, () => {
43
+ if (check()) {
44
+ clearTimeout(timer);
45
+ fs.unwatchFile(filePath);
46
+ resolve();
47
+ }
48
+ });
49
+ });
50
+ }
51
+ export async function startDaemon(mcpArgs = [], sessionId) {
52
+ if (isDaemonRunning(sessionId)) {
53
+ logger?.('Daemon is already running');
54
+ return;
55
+ }
56
+ const pidFilePath = getPidFilePath(sessionId);
57
+ if (fs.existsSync(pidFilePath)) {
58
+ fs.unlinkSync(pidFilePath);
59
+ }
60
+ logger?.('Starting daemon...', ...mcpArgs);
61
+ const child = spawn(process.execPath, [DAEMON_SCRIPT_PATH, ...mcpArgs], {
62
+ detached: true,
63
+ stdio: 'ignore',
64
+ env: { ...process.env, CHROME_DEVTOOLS_MCP_SESSION_ID: sessionId },
65
+ cwd: process.cwd(),
66
+ windowsHide: true,
67
+ });
68
+ child.unref();
69
+ await waitForFile(pidFilePath);
70
+ }
71
+ const SEND_COMMAND_TIMEOUT = 60_000; // ms
72
+ /**
73
+ * `sendCommand` opens a socket connection sends a single command and disconnects.
74
+ */
75
+ export async function sendCommand(command, sessionId) {
76
+ const socketPath = getSocketPath(sessionId);
77
+ const socket = net.createConnection({
78
+ path: socketPath,
79
+ });
80
+ return new Promise((resolve, reject) => {
81
+ const timer = setTimeout(() => {
82
+ socket.destroy();
83
+ reject(new Error('Timeout waiting for daemon response'));
84
+ }, SEND_COMMAND_TIMEOUT);
85
+ const transport = new PipeTransport(socket, socket);
86
+ transport.onmessage = async (message) => {
87
+ clearTimeout(timer);
88
+ logger?.('onmessage', message);
89
+ resolve(JSON.parse(message));
90
+ };
91
+ socket.on('error', error => {
92
+ clearTimeout(timer);
93
+ logger?.('Socket error:', error);
94
+ reject(error);
95
+ });
96
+ socket.on('close', () => {
97
+ clearTimeout(timer);
98
+ logger?.('Socket closed:');
99
+ reject(new Error('Socket closed'));
100
+ });
101
+ logger?.('Sending message', command);
102
+ transport.send(JSON.stringify(command));
103
+ });
104
+ }
105
+ export async function stopDaemon(sessionId) {
106
+ if (!isDaemonRunning(sessionId)) {
107
+ logger?.('Daemon is not running');
108
+ return;
109
+ }
110
+ const pidFilePath = getPidFilePath(sessionId);
111
+ await sendCommand({ method: 'stop' }, sessionId);
112
+ await waitForFile(pidFilePath, /*removed=*/ true);
113
+ }
114
+ export async function handleResponse(response, format) {
115
+ if (response.isError) {
116
+ return JSON.stringify(response.content);
117
+ }
118
+ const chunks = [];
119
+ const images = [];
120
+ for (const content of response.content) {
121
+ if (content.type === 'text') {
122
+ chunks.push(content.text);
123
+ }
124
+ else if (content.type === 'image') {
125
+ const imageData = content.data;
126
+ const mimeType = content.mimeType;
127
+ let extension = '.png';
128
+ switch (mimeType) {
129
+ case 'image/jpg':
130
+ case 'image/jpeg':
131
+ extension = '.jpeg';
132
+ break;
133
+ case 'image/webp':
134
+ extension = '.webp';
135
+ break;
136
+ }
137
+ const data = Buffer.from(imageData, 'base64');
138
+ const name = crypto.randomUUID();
139
+ const filepath = await getTempFilePath(`${name}${extension}`);
140
+ fs.writeFileSync(filepath, data);
141
+ images.push({ filePath: filepath, mimeType });
142
+ chunks.push(`Saved to ${filepath}.`);
143
+ }
144
+ else {
145
+ throw new Error('Not supported response content type');
146
+ }
147
+ }
148
+ if (format === 'json') {
149
+ if (response.structuredContent) {
150
+ const structuredContent = {
151
+ ...response.structuredContent,
152
+ ...(images.length ? { images } : {}),
153
+ };
154
+ return JSON.stringify(structuredContent);
155
+ }
156
+ // Fall-through to text for backward compatibility.
157
+ }
158
+ return format === 'md' ? chunks.join(' ') : JSON.stringify(chunks);
159
+ }
160
+ //# sourceMappingURL=client.js.map