opera-devtools-mcp 0.2.4 → 0.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 (330) hide show
  1. package/README.md +104 -7
  2. package/build/src/McpContext.js +365 -365
  3. package/build/src/McpPage.js +475 -48
  4. package/build/src/McpResponse.js +462 -288
  5. package/build/src/SlimMcpResponse.js +1 -1
  6. package/build/src/TextSnapshot.js +101 -22
  7. package/build/src/ToolHandler.js +142 -20
  8. package/build/src/bin/check-latest-version.js +27 -1
  9. package/build/src/bin/chrome-devtools-cli-options.js +573 -10
  10. package/build/src/bin/chrome-devtools-mcp-cli-options.js +119 -32
  11. package/build/src/bin/chrome-devtools-mcp-main.js +50 -7
  12. package/build/src/bin/chrome-devtools-mcp.js +7 -4
  13. package/build/src/bin/chrome-devtools.js +57 -14
  14. package/build/src/bin/opera-devtools-cli-options.js +3 -829
  15. package/build/src/bin/opera-devtools-mcp-cli-options.js +3 -348
  16. package/build/src/bin/opera-devtools-mcp-main.js +3 -37
  17. package/build/src/bin/opera-devtools-mcp.js +4 -19
  18. package/build/src/bin/opera-devtools.js +4 -185
  19. package/build/src/browser.js +45 -5
  20. package/build/src/{PageCollector.js → collectors/PageCollector.js} +64 -133
  21. package/build/src/collectors/ServiceWorkerCollector.js +171 -0
  22. package/build/src/daemon/client.js +75 -17
  23. package/build/src/daemon/daemon.js +92 -30
  24. package/build/src/daemon/utils.js +24 -8
  25. package/build/src/{DevtoolsUtils.js → devtools/DevtoolsUtils.js} +94 -88
  26. package/build/src/devtools/McpHostBindingAdapter.js +165 -0
  27. package/build/src/{issue-descriptions.js → devtools/issueDescriptions.js} +8 -6
  28. package/build/src/formatters/ConsoleFormatter.js +44 -11
  29. package/build/src/formatters/HeapSnapshotFormatter.js +140 -9
  30. package/build/src/formatters/IssueFormatter.js +7 -7
  31. package/build/src/formatters/NetworkFormatter.js +10 -8
  32. package/build/src/index.js +101 -232
  33. package/build/src/opera/branding.js +34 -0
  34. package/build/src/opera/browserFlags.js +85 -0
  35. package/build/src/opera/browserLaunch.js +52 -0
  36. package/build/src/opera/policy.js +50 -0
  37. package/build/src/opera/serviceWorkerRetry.js +37 -0
  38. package/build/src/opera/toolHandlerHooks.js +39 -0
  39. package/build/src/{tools → opera/tools}/opera.js +9 -21
  40. package/build/src/processors/HeapSnapshotManager.js +258 -0
  41. package/build/src/{trace-processing/parse.js → processors/PerformanceTrace.js} +9 -9
  42. package/build/src/telemetry/ClearcutLogger.js +12 -119
  43. package/build/src/telemetry/WatchdogClient.js +5 -5
  44. package/build/src/telemetry/errors.js +1 -1
  45. package/build/src/telemetry/flagUtils.js +6 -6
  46. package/build/src/telemetry/{toolMetricsUtils.js → metricsRegistry.js} +4 -11
  47. package/build/src/telemetry/persistence.js +3 -3
  48. package/build/src/telemetry/transformation.js +184 -0
  49. package/build/src/telemetry/watchdog/ClearcutSender.js +11 -11
  50. package/build/src/telemetry/watchdog/main.js +6 -6
  51. package/build/src/third_party/THIRD_PARTY_NOTICES +232 -1061
  52. package/build/src/third_party/bundled-packages.json +6 -8
  53. package/build/src/third_party/devtools-formatter-worker.js +63 -22
  54. package/build/src/third_party/devtools-heap-snapshot-worker.js +809 -182
  55. package/build/src/third_party/index.js +69558 -74691
  56. package/build/src/third_party/issue-descriptions/CoepCorpNotSameOrigin.md +2 -2
  57. package/build/src/third_party/issue-descriptions/CoepCorpNotSameOriginAfterDefaultedToSameOriginByCoep.md +3 -3
  58. package/build/src/third_party/issue-descriptions/CoepCorpNotSameSite.md +2 -2
  59. package/build/src/third_party/issue-descriptions/CoepFrameResourceNeedsCoepHeader.md +1 -1
  60. package/build/src/third_party/issue-descriptions/CompatibilityModeQuirks.md +1 -1
  61. package/build/src/third_party/issue-descriptions/CookieAttributeValueExceedsMaxSize.md +2 -2
  62. package/build/src/third_party/issue-descriptions/LowTextContrast.md +1 -1
  63. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorRead.md +1 -1
  64. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorSet.md +1 -1
  65. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnRead.md +1 -1
  66. package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnSet.md +1 -1
  67. package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeRead.md +3 -3
  68. package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeSet.md +3 -3
  69. package/build/src/third_party/issue-descriptions/bounceTrackingMitigations.md +1 -1
  70. package/build/src/third_party/issue-descriptions/connectionAllowlistEmbeddingRequirementNotSatisfied.md +26 -0
  71. package/build/src/third_party/issue-descriptions/connectionAllowlistIFrameAttributeLoosensEmbeddingRequirement.md +19 -0
  72. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowConnectionAllowlistFrom.md +21 -0
  73. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowlistItemType.md +2 -2
  74. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidHeader.md +2 -2
  75. package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidUrlPattern.md +3 -3
  76. package/build/src/third_party/issue-descriptions/connectionAllowlistItemNotInnerList.md +2 -2
  77. package/build/src/third_party/issue-descriptions/connectionAllowlistMoreThanOneList.md +2 -2
  78. package/build/src/third_party/issue-descriptions/connectionAllowlistReportingEndpointNotToken.md +1 -1
  79. package/build/src/third_party/issue-descriptions/cookieCrossSiteRedirectDowngrade.md +3 -3
  80. package/build/src/third_party/issue-descriptions/cookieExcludeBlockedWithinRelatedWebsiteSet.md +1 -1
  81. package/build/src/third_party/issue-descriptions/cookieExcludePortMismatch.md +1 -1
  82. package/build/src/third_party/issue-descriptions/cookieExcludeSchemeMismatch.md +1 -1
  83. package/build/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutSet.md +1 -1
  84. package/build/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutRead.md +1 -1
  85. package/build/src/third_party/issue-descriptions/corsAllowCredentialsRequired.md +1 -1
  86. package/build/src/third_party/issue-descriptions/corsDisabledScheme.md +2 -2
  87. package/build/src/third_party/issue-descriptions/corsDisallowedByMode.md +2 -2
  88. package/build/src/third_party/issue-descriptions/corsHeaderDisallowedByPreflightResponse.md +1 -1
  89. package/build/src/third_party/issue-descriptions/corsInvalidHeaderValues.md +3 -3
  90. package/build/src/third_party/issue-descriptions/corsLocalNetworkAccessPermissionDenied.md +1 -1
  91. package/build/src/third_party/issue-descriptions/corsMethodDisallowedByPreflightResponse.md +1 -1
  92. package/build/src/third_party/issue-descriptions/corsNoCorsRedirectModeNotFollow.md +1 -1
  93. package/build/src/third_party/issue-descriptions/corsOriginMismatch.md +3 -3
  94. package/build/src/third_party/issue-descriptions/corsPreflightResponseInvalid.md +2 -2
  95. package/build/src/third_party/issue-descriptions/corsRedirectContainsCredentials.md +3 -3
  96. package/build/src/third_party/issue-descriptions/corsWildcardOriginNotAllowed.md +4 -3
  97. package/build/src/third_party/issue-descriptions/cspEvalViolation.md +4 -4
  98. package/build/src/third_party/issue-descriptions/cspInlineViolation.md +2 -2
  99. package/build/src/third_party/issue-descriptions/cspTrustedTypesPolicyViolation.md +2 -2
  100. package/build/src/third_party/issue-descriptions/cspTrustedTypesSinkViolation.md +4 -4
  101. package/build/src/third_party/issue-descriptions/cspURLViolation.md +4 -4
  102. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsEmptyList.md +1 -0
  103. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsHttpNotFound.md +1 -0
  104. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidContentType.md +1 -0
  105. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidResponse.md +1 -0
  106. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsNoResponse.md +1 -0
  107. package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsFetchFailed.md +1 -0
  108. package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsInvalidRecord.md +1 -0
  109. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownHttpNotFound.md +1 -0
  110. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidContentType.md +1 -0
  111. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidResponse.md +1 -0
  112. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownNoResponse.md +1 -0
  113. package/build/src/third_party/issue-descriptions/emailVerificationRequestInvalidEmail.md +1 -0
  114. package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksHttpNotFound.md +1 -0
  115. package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksInvalidResponse.md +1 -0
  116. package/build/src/third_party/issue-descriptions/emailVerificationRequestKeyBindingSigningFailed.md +1 -0
  117. package/build/src/third_party/issue-descriptions/emailVerificationRequestRpOriginIsOpaque.md +1 -0
  118. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenHttpNotFound.md +1 -0
  119. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidContentType.md +1 -0
  120. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidResponse.md +1 -0
  121. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidSdJwt.md +1 -0
  122. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenMalformedSdJwt.md +1 -0
  123. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenNoResponse.md +1 -0
  124. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidAudience.md +1 -0
  125. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidIssuedAt.md +1 -0
  126. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidNonce.md +1 -0
  127. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidSdHash.md +1 -0
  128. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidTyp.md +1 -0
  129. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingAud.md +1 -0
  130. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingCnf.md +1 -0
  131. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingIat.md +1 -0
  132. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingNonce.md +1 -0
  133. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingSdHash.md +1 -0
  134. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbSignatureFailed.md +1 -0
  135. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmail.md +1 -0
  136. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmailVerified.md +1 -0
  137. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidHolderKey.md +1 -0
  138. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuedAt.md +1 -0
  139. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuer.md +1 -0
  140. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtJwksMissingKeys.md +1 -0
  141. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingCnf.md +1 -0
  142. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingEmail.md +1 -0
  143. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIat.md +1 -0
  144. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIss.md +1 -0
  145. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtSignatureFailed.md +1 -0
  146. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtUnsupportedHeaderAlg.md +1 -0
  147. package/build/src/third_party/issue-descriptions/emailVerificationRequestUserLoggedOut.md +1 -0
  148. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownAccountsEndpointCrossOrigin.md +1 -0
  149. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownHttpNotFound.md +1 -0
  150. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidContentType.md +1 -0
  151. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidResponse.md +1 -0
  152. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownIssuanceEndpointCrossOrigin.md +1 -0
  153. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownListEmpty.md +1 -0
  154. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingAccountsEndpoint.md +1 -0
  155. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingIssuanceEndpoint.md +1 -0
  156. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownNoResponse.md +1 -0
  157. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownUnsupportedSigningAlgorithm.md +1 -0
  158. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsHttpNotFound.md +1 -1
  159. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsInvalidResponse.md +1 -1
  160. package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsNoResponse.md +1 -1
  161. package/build/src/third_party/issue-descriptions/federatedAuthRequestApprovalDeclined.md +1 -1
  162. package/build/src/third_party/issue-descriptions/federatedAuthRequestCanceled.md +1 -1
  163. package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorFetchingSignin.md +1 -1
  164. package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorIdToken.md +1 -1
  165. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenHttpNotFound.md +1 -1
  166. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidRequest.md +1 -1
  167. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidResponse.md +1 -1
  168. package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenNoResponse.md +1 -1
  169. package/build/src/third_party/issue-descriptions/federatedAuthRequestInvalidSigninResponse.md +1 -1
  170. package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestHttpNotFound.md +1 -1
  171. package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestInvalidResponse.md +1 -1
  172. package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestNoResponse.md +1 -1
  173. package/build/src/third_party/issue-descriptions/federatedAuthRequestTooManyRequests.md +1 -1
  174. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidAccountsResponse.md +1 -1
  175. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidConfigOrWellKnown.md +1 -1
  176. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoAccountSharingPermission.md +1 -1
  177. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoApiPermission.md +1 -1
  178. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoReturningUserFromFetchedAccounts.md +1 -1
  179. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotIframe.md +1 -1
  180. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotPotentiallyTrustworthy.md +1 -1
  181. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSameOrigin.md +1 -1
  182. package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSignedInWithIdp.md +1 -1
  183. package/build/src/third_party/issue-descriptions/fetchingPartitionedBlobURL.md +2 -2
  184. package/build/src/third_party/issue-descriptions/genericBackUINavigationWouldSkipAd.md +4 -0
  185. package/build/src/third_party/issue-descriptions/genericFormAriaLabelledByToNonExistingIdError.md +2 -2
  186. package/build/src/third_party/issue-descriptions/genericFormAutocompleteAttributeEmptyError.md +1 -1
  187. package/build/src/third_party/issue-descriptions/genericFormDuplicateIdForInputError.md +1 -1
  188. package/build/src/third_party/issue-descriptions/genericFormEmptyIdAndNameAttributesForInputError.md +2 -2
  189. package/build/src/third_party/issue-descriptions/genericFormInputAssignedAutocompleteValueToIdOrNameAttributeError.md +2 -2
  190. package/build/src/third_party/issue-descriptions/genericFormInputHasWrongButWellIntendedAutocompleteValueError.md +1 -1
  191. package/build/src/third_party/issue-descriptions/genericFormInputWithNoLabelError.md +1 -1
  192. package/build/src/third_party/issue-descriptions/genericFormLabelForMatchesNonExistingIdError.md +2 -2
  193. package/build/src/third_party/issue-descriptions/genericFormLabelForNameError.md +1 -1
  194. package/build/src/third_party/issue-descriptions/genericFormLabelHasNeitherForNorNestedInputError.md +1 -1
  195. package/build/src/third_party/issue-descriptions/genericFormModelContextMissingToolName.md +1 -1
  196. package/build/src/third_party/issue-descriptions/genericFormModelContextParameterMissingName.md +1 -1
  197. package/build/src/third_party/issue-descriptions/genericFormModelContextRequiredParameterMissingName.md +1 -1
  198. package/build/src/third_party/issue-descriptions/genericNavigationEntryMarkedSkippable.md +2 -2
  199. package/build/src/third_party/issue-descriptions/heavyAd.md +1 -1
  200. package/build/src/third_party/issue-descriptions/lazyLoadImageZeroSize.md +7 -0
  201. package/build/src/third_party/issue-descriptions/navigatingPartitionedBlobURL.md +3 -3
  202. package/build/src/third_party/issue-descriptions/permissionElementActivationDisabled.md +1 -1
  203. package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluder.md +1 -1
  204. package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluderParent.md +1 -1
  205. package/build/src/third_party/issue-descriptions/permissionElementFencedFrameDisallowed.md +2 -2
  206. package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooLarge.md +2 -2
  207. package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooSmall.md +2 -2
  208. package/build/src/third_party/issue-descriptions/permissionElementInsetBoxShadowUnsupported.md +1 -1
  209. package/build/src/third_party/issue-descriptions/permissionElementInvalidDisplayStyle.md +2 -2
  210. package/build/src/third_party/issue-descriptions/permissionElementInvalidSizeValue.md +2 -2
  211. package/build/src/third_party/issue-descriptions/permissionElementInvalidType.md +2 -2
  212. package/build/src/third_party/issue-descriptions/permissionElementInvalidTypeActivation.md +2 -2
  213. package/build/src/third_party/issue-descriptions/permissionElementNonOpaqueColor.md +1 -1
  214. package/build/src/third_party/issue-descriptions/permissionElementPaddingBottomUnsupported.md +2 -2
  215. package/build/src/third_party/issue-descriptions/permissionElementPaddingRightUnsupported.md +2 -2
  216. package/build/src/third_party/issue-descriptions/permissionElementRequestInProgress.md +2 -2
  217. package/build/src/third_party/issue-descriptions/permissionElementSecurityChecksFailed.md +1 -1
  218. package/build/src/third_party/issue-descriptions/permissionElementTypeNotSupported.md +2 -2
  219. package/build/src/third_party/issue-descriptions/permissionElementUntrustedEvent.md +1 -1
  220. package/build/src/third_party/issue-descriptions/placeholderDescriptionForInvisibleIssues.md +1 -1
  221. package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedOptGroupChild.md +1 -1
  222. package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedSelectChild.md +1 -1
  223. package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentAttributesSelectDescendant.md +1 -1
  224. package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentLegendChild.md +1 -1
  225. package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentOptionChild.md +1 -1
  226. package/build/src/third_party/issue-descriptions/selectElementAccessibilityNonPhrasingContentOptionChild.md +1 -1
  227. package/build/src/third_party/issue-descriptions/selectivePermissionsIntervention.md +3 -3
  228. package/build/src/third_party/issue-descriptions/sharedArrayBuffer.md +1 -1
  229. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorCrossOriginNoCorsRequest.md +1 -1
  230. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorMatchingDictionaryNotUsed.md +1 -1
  231. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorUnexpectedContentDictionaryHeader.md +1 -1
  232. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorCossOriginNoCorsRequest.md +1 -1
  233. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorDisallowedBySettings.md +1 -1
  234. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorExpiredResponse.md +1 -1
  235. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorFeatureDisabled.md +2 -2
  236. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidStructuredHeader.md +1 -1
  237. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNavigationRequest.md +2 -2
  238. package/build/src/third_party/issue-descriptions/sriInvalidSignatureHeader.md +4 -4
  239. package/build/src/third_party/issue-descriptions/sriInvalidSignatureInputHeader.md +4 -4
  240. package/build/src/third_party/issue-descriptions/sriMissingSignatureHeader.md +1 -1
  241. package/build/src/third_party/issue-descriptions/sriMissingSignatureInputHeader.md +2 -2
  242. package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsIncorrectLength.md +2 -2
  243. package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsNotByteSequence.md +4 -4
  244. package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsParameterized.md +4 -4
  245. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentName.md +1 -1
  246. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentType.md +2 -2
  247. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidDerivedComponentParameter.md +1 -1
  248. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidHeaderComponentParameter.md +1 -1
  249. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidParameter.md +2 -2
  250. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderKeyIdLength.md +5 -5
  251. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingLabel.md +3 -3
  252. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingRequiredParameters.md +2 -2
  253. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueMissingComponents.md +2 -2
  254. package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueNotInnerList.md +2 -2
  255. package/build/src/third_party/issue-descriptions/sriValidationFailedIntegrityMismatch.md +4 -4
  256. package/build/src/third_party/issue-descriptions/sriValidationFailedInvalidLength.md +3 -3
  257. package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureExpired.md +2 -2
  258. package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureMismatch.md +2 -2
  259. package/build/src/third_party/issue-descriptions/stylesheetLateImport.md +1 -1
  260. package/build/src/third_party/issue-descriptions/summaryElementAccessibilityInteractiveContentSummaryDescendant.md +1 -1
  261. package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestLength.md +2 -2
  262. package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestType.md +3 -3
  263. package/build/src/third_party/issue-descriptions/unencodedDigestMalformedDictionary.md +2 -2
  264. package/build/src/third_party/issue-descriptions/unencodedDigestUnknownAlgorithm.md +1 -1
  265. package/build/src/third_party/lighthouse-devtools-mcp-bundle.js +3177 -6340
  266. package/build/src/tools/ToolDefinition.js +2 -2
  267. package/build/src/tools/categories.js +3 -0
  268. package/build/src/tools/console.js +13 -0
  269. package/build/src/tools/emulation.js +30 -3
  270. package/build/src/tools/extensions.js +8 -1
  271. package/build/src/tools/input.js +246 -128
  272. package/build/src/tools/lighthouse.js +14 -7
  273. package/build/src/tools/memory.js +289 -25
  274. package/build/src/tools/network.js +10 -7
  275. package/build/src/tools/pages.js +33 -92
  276. package/build/src/tools/performance.js +67 -52
  277. package/build/src/tools/pwa.js +128 -0
  278. package/build/src/tools/screencast.js +35 -12
  279. package/build/src/tools/screenshot.js +234 -75
  280. package/build/src/tools/script.js +130 -45
  281. package/build/src/tools/slim/tools.js +3 -0
  282. package/build/src/tools/snapshot.js +7 -4
  283. package/build/src/tools/thirdPartyDeveloper.js +19 -9
  284. package/build/src/tools/tools.js +3 -1
  285. package/build/src/tools/webmcp.js +3 -1
  286. package/build/src/utils/WaitForHelper.js +299 -0
  287. package/build/src/utils/check-for-updates.js +8 -2
  288. package/build/src/utils/files.js +42 -3
  289. package/build/src/utils/logger.js +51 -0
  290. package/build/src/utils/polyfill.js +11 -0
  291. package/build/src/utils/url.js +43 -0
  292. package/build/src/version.js +1 -1
  293. package/package.json +42 -33
  294. package/build/src/DevToolsConnectionAdapter.js +0 -70
  295. package/build/src/HeapSnapshotManager.js +0 -110
  296. package/build/src/Mutex.js +0 -38
  297. package/build/src/WaitForHelper.js +0 -195
  298. package/build/src/bin/cliDefinitions.js +0 -616
  299. package/build/src/logger.js +0 -37
  300. package/build/src/polyfill.js +0 -8
  301. package/build/src/telemetry/metricUtils.js +0 -15
  302. package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeRead.md +0 -8
  303. package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeSet.md +0 -8
  304. package/build/src/third_party/issue-descriptions/SameSiteExcludeNavigationContextDowngrade.md +0 -8
  305. package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeRead.md +0 -8
  306. package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeSet.md +0 -8
  307. package/build/src/third_party/issue-descriptions/SameSiteWarnStrictLaxDowngradeStrict.md +0 -8
  308. package/build/src/third_party/issue-descriptions/arInsecureContext.md +0 -7
  309. package/build/src/third_party/issue-descriptions/arInvalidInfoHeader.md +0 -5
  310. package/build/src/third_party/issue-descriptions/arInvalidRegisterOsSourceHeader.md +0 -5
  311. package/build/src/third_party/issue-descriptions/arInvalidRegisterOsTriggerHeader.md +0 -5
  312. package/build/src/third_party/issue-descriptions/arInvalidRegisterSourceHeader.md +0 -5
  313. package/build/src/third_party/issue-descriptions/arInvalidRegisterTriggerHeader.md +0 -5
  314. package/build/src/third_party/issue-descriptions/arNavigationRegistrationUniqueScopeAlreadySet.md +0 -5
  315. package/build/src/third_party/issue-descriptions/arNavigationRegistrationWithoutTransientUserActivation.md +0 -6
  316. package/build/src/third_party/issue-descriptions/arNoRegisterOsSourceHeader.md +0 -5
  317. package/build/src/third_party/issue-descriptions/arNoRegisterOsTriggerHeader.md +0 -5
  318. package/build/src/third_party/issue-descriptions/arNoRegisterSourceHeader.md +0 -5
  319. package/build/src/third_party/issue-descriptions/arNoRegisterTriggerHeader.md +0 -5
  320. package/build/src/third_party/issue-descriptions/arNoWebOrOsSupport.md +0 -4
  321. package/build/src/third_party/issue-descriptions/arOsSourceIgnored.md +0 -18
  322. package/build/src/third_party/issue-descriptions/arOsTriggerIgnored.md +0 -19
  323. package/build/src/third_party/issue-descriptions/arPermissionPolicyDisabled.md +0 -8
  324. package/build/src/third_party/issue-descriptions/arSourceAndTriggerHeaders.md +0 -9
  325. package/build/src/third_party/issue-descriptions/arSourceIgnored.md +0 -13
  326. package/build/src/third_party/issue-descriptions/arTriggerIgnored.md +0 -12
  327. package/build/src/third_party/issue-descriptions/arUntrustworthyReportingOrigin.md +0 -10
  328. package/build/src/third_party/issue-descriptions/arWebAndOsHeaders.md +0 -11
  329. package/build/src/utils/string.js +0 -37
  330. package/build/src/utils/types.js +0 -7
@@ -6,110 +6,18 @@
6
6
  * Modified by Opera Software AS.
7
7
  */
8
8
  import { ConsoleFormatter } from './formatters/ConsoleFormatter.js';
9
- import { HeapSnapshotFormatter } from './formatters/HeapSnapshotFormatter.js';
10
- import { isNodeLike } from './formatters/HeapSnapshotFormatter.js';
9
+ import { HeapSnapshotFormatter, isEdgeLike, isNodeLike, } from './formatters/HeapSnapshotFormatter.js';
11
10
  import { IssueFormatter } from './formatters/IssueFormatter.js';
12
11
  import { NetworkFormatter } from './formatters/NetworkFormatter.js';
13
12
  import { SnapshotFormatter } from './formatters/SnapshotFormatter.js';
14
- import { UncaughtError } from './PageCollector.js';
13
+ import { UncaughtError } from './collectors/PageCollector.js';
15
14
  import { TextSnapshot } from './TextSnapshot.js';
16
- import { DevTools } from './third_party/index.js';
17
- import { handleDialog } from './tools/pages.js';
18
- import { getInsightOutput, getTraceSummary } from './trace-processing/parse.js';
15
+ import { DevTools, getToonEncode, getGcfEncode } from './third_party/index.js';
16
+ import { handleDialog, listPages } from './tools/pages.js';
17
+ import { getInsightOutput, getTraceSummary, } from './processors/PerformanceTrace.js';
18
+ import { stableIdSymbol } from './utils/id.js';
19
19
  import { paginate } from './utils/pagination.js';
20
- export function replaceHtmlElementsWithUids(schema) {
21
- if (typeof schema === 'boolean') {
22
- return;
23
- }
24
- let isHtmlElement = false;
25
- for (const [key, value] of Object.entries(schema)) {
26
- if (key === 'x-mcp-type' && value === 'HTMLElement') {
27
- isHtmlElement = true;
28
- break;
29
- }
30
- }
31
- if (isHtmlElement) {
32
- schema.properties = { uid: { type: 'string' } };
33
- schema.required = ['uid'];
34
- }
35
- if (schema.properties) {
36
- for (const key of Object.keys(schema.properties)) {
37
- replaceHtmlElementsWithUids(schema.properties[key]);
38
- }
39
- }
40
- if (schema.items) {
41
- if (Array.isArray(schema.items)) {
42
- for (const item of schema.items) {
43
- replaceHtmlElementsWithUids(item);
44
- }
45
- }
46
- else {
47
- replaceHtmlElementsWithUids(schema.items);
48
- }
49
- }
50
- if (schema.anyOf) {
51
- for (const s of schema.anyOf) {
52
- replaceHtmlElementsWithUids(s);
53
- }
54
- }
55
- if (schema.allOf) {
56
- for (const s of schema.allOf) {
57
- replaceHtmlElementsWithUids(s);
58
- }
59
- }
60
- if (schema.oneOf) {
61
- for (const s of schema.oneOf) {
62
- replaceHtmlElementsWithUids(s);
63
- }
64
- }
65
- }
66
- async function getToolGroup(page) {
67
- // Check if there is a `devtoolstooldiscovery` event listener
68
- const windowHandle = await page.pptrPage.evaluateHandle(() => window);
69
- // @ts-expect-error internal API
70
- const client = page.pptrPage._client();
71
- const { listeners } = await client.send('DOMDebugger.getEventListeners', {
72
- objectId: windowHandle.remoteObject().objectId,
73
- });
74
- if (listeners.find(l => l.type === 'devtoolstooldiscovery') === undefined) {
75
- return;
76
- }
77
- const toolGroup = await page.pptrPage.evaluate(() => {
78
- return new Promise(resolve => {
79
- const event = new CustomEvent('devtoolstooldiscovery');
80
- // @ts-expect-error Adding custom property
81
- event.respondWith = (toolGroup) => {
82
- if (!window.__dtmcp) {
83
- window.__dtmcp = {};
84
- }
85
- window.__dtmcp.toolGroup = toolGroup;
86
- // When receiving a toolGroup for the first time, expose a simple execution helper
87
- if (!window.__dtmcp.executeTool) {
88
- window.__dtmcp.executeTool = async (toolName, args) => {
89
- if (!window.__dtmcp?.toolGroup) {
90
- throw new Error('No tools found on the page');
91
- }
92
- const tool = window.__dtmcp.toolGroup.tools.find(t => t.name === toolName);
93
- if (!tool) {
94
- throw new Error(`Tool ${toolName} not found`);
95
- }
96
- return await tool.execute(args);
97
- };
98
- }
99
- resolve(toolGroup);
100
- };
101
- window.dispatchEvent(event);
102
- // If the page does not synchronously call `event.respondWith`, return instead of timing out
103
- setTimeout(() => {
104
- resolve(null);
105
- }, 0);
106
- });
107
- });
108
- for (const tool of toolGroup?.tools ?? []) {
109
- replaceHtmlElementsWithUids(tool.inputSchema);
110
- }
111
- return toolGroup;
112
- }
20
+ const { formatBytesToKb } = DevTools.I18n.ByteUtilities;
113
21
  export class McpResponse {
114
22
  #includePages = false;
115
23
  #includeExtensionServiceWorkers = false;
@@ -136,6 +44,11 @@ export class McpResponse {
136
44
  #redactNetworkHeaders = true;
137
45
  #error;
138
46
  #logCallback;
47
+ #attachedWaitForResult;
48
+ #reconnectNotice = false;
49
+ get #deviceScope() {
50
+ return this.#page?.viewport?.isMobile ? 'PHONE' : 'DESKTOP';
51
+ }
139
52
  constructor(args, logCallback) {
140
53
  this.#args = args;
141
54
  this.#logCallback = logCallback;
@@ -149,6 +62,13 @@ export class McpResponse {
149
62
  setRedactNetworkHeaders(value) {
150
63
  this.#redactNetworkHeaders = value;
151
64
  }
65
+ /**
66
+ * Surfaces a one-time note that the browser reconnected and page ids changed.
67
+ * Set by the tool handler when the context reports a pending reconnect notice.
68
+ */
69
+ setReconnectNotice() {
70
+ this.#reconnectNotice = true;
71
+ }
152
72
  attachDevToolsData(data) {
153
73
  this.#devToolsData = data;
154
74
  }
@@ -183,7 +103,7 @@ export class McpResponse {
183
103
  }
184
104
  this.#networkRequestsOptions = {
185
105
  include: value,
186
- pagination: options?.pageSize || options?.pageIdx
106
+ pagination: options?.pageSize !== undefined || options?.pageIdx !== undefined
187
107
  ? {
188
108
  pageSize: options.pageSize,
189
109
  pageIdx: options.pageIdx,
@@ -201,7 +121,7 @@ export class McpResponse {
201
121
  }
202
122
  this.#consoleDataOptions = {
203
123
  include: value,
204
- pagination: options?.pageSize || options?.pageIdx
124
+ pagination: options?.pageSize !== undefined || options?.pageIdx !== undefined
205
125
  ? {
206
126
  pageSize: options.pageSize,
207
127
  pageIdx: options.pageIdx,
@@ -209,6 +129,8 @@ export class McpResponse {
209
129
  : undefined,
210
130
  types: options?.types,
211
131
  includePreservedMessages: options?.includePreservedMessages,
132
+ includeStackTraces: options?.includeStackTraces,
133
+ serviceWorkerId: options?.serviceWorkerId,
212
134
  };
213
135
  }
214
136
  setError(error) {
@@ -270,20 +192,25 @@ export class McpResponse {
270
192
  appendResponseLine(value) {
271
193
  this.#textResponseLines.push(value);
272
194
  }
273
- setHeapSnapshotAggregates(aggregates, options) {
195
+ attachWaitForResult(result) {
196
+ this.#attachedWaitForResult = result;
197
+ }
198
+ setHeapSnapshotAggregates(aggregateData, options) {
274
199
  this.#heapSnapshotOptions = {
275
200
  ...this.#heapSnapshotOptions,
276
201
  include: true,
277
- aggregates,
202
+ aggregateData,
278
203
  pagination: options,
279
204
  };
280
205
  }
281
- setHeapSnapshotStats(stats, staticData) {
206
+ setHeapSnapshotStats(stats, staticData, nativeContextSizes, retainedByContextSummary) {
282
207
  this.#heapSnapshotOptions = {
283
208
  ...this.#heapSnapshotOptions,
284
209
  include: true,
285
210
  stats,
286
211
  staticData,
212
+ nativeContextSizes,
213
+ retainedByContextSummary,
287
214
  };
288
215
  }
289
216
  setHeapSnapshotNodes(nodes, options) {
@@ -294,6 +221,49 @@ export class McpResponse {
294
221
  pagination: options,
295
222
  };
296
223
  }
224
+ setHeapSnapshotDuplicateStrings(duplicateStrings, options) {
225
+ this.#heapSnapshotOptions = {
226
+ ...this.#heapSnapshotOptions,
227
+ include: true,
228
+ duplicateStrings,
229
+ pagination: options,
230
+ };
231
+ }
232
+ setHeapSnapshotRetainingPaths(retainingPaths) {
233
+ this.#heapSnapshotOptions = {
234
+ ...this.#heapSnapshotOptions,
235
+ include: true,
236
+ retainingPaths,
237
+ };
238
+ }
239
+ setHeapSnapshotDominators(dominators) {
240
+ this.#heapSnapshotOptions = {
241
+ ...this.#heapSnapshotOptions,
242
+ include: true,
243
+ dominators,
244
+ };
245
+ }
246
+ setHeapSnapshotClassDiffs(classDiffs) {
247
+ this.#heapSnapshotOptions = {
248
+ ...this.#heapSnapshotOptions,
249
+ include: true,
250
+ classDiffs,
251
+ };
252
+ }
253
+ setHeapSnapshotDetailedClassDiff(detailedClassDiff) {
254
+ this.#heapSnapshotOptions = {
255
+ ...this.#heapSnapshotOptions,
256
+ include: true,
257
+ detailedClassDiff,
258
+ };
259
+ }
260
+ setHeapSnapshotObjectDetails(objectInfo) {
261
+ this.#heapSnapshotOptions = {
262
+ ...this.#heapSnapshotOptions,
263
+ include: true,
264
+ objectInfo,
265
+ };
266
+ }
297
267
  attachImage(value) {
298
268
  this.#images.push(value);
299
269
  }
@@ -309,167 +279,189 @@ export class McpResponse {
309
279
  get listWebMcpTools() {
310
280
  return this.#listWebMcpTools;
311
281
  }
312
- async handle(toolName, context) {
282
+ async #handleSnapshot(context) {
313
283
  if (this.#includePages) {
314
284
  await context.createPagesSnapshot();
315
285
  }
316
- if (this.#includeExtensionServiceWorkers) {
317
- await context.createExtensionServiceWorkersSnapshot();
286
+ if (!this.#snapshotParams) {
287
+ return undefined;
318
288
  }
319
- let snapshot;
320
- if (this.#snapshotParams) {
321
- if (!this.#page) {
322
- throw new Error('Response must have a page');
323
- }
324
- this.#page.textSnapshot = await TextSnapshot.create(this.#page, {
325
- verbose: this.#snapshotParams.verbose,
326
- devtoolsData: this.#devToolsData,
327
- });
328
- const textSnapshot = this.#page.textSnapshot;
329
- if (textSnapshot) {
330
- const formatter = new SnapshotFormatter(textSnapshot);
331
- if (this.#snapshotParams.filePath) {
332
- const result = await context.saveFile(new TextEncoder().encode(formatter.toString()), this.#snapshotParams.filePath, '.txt');
333
- snapshot = result.filename;
334
- }
335
- else {
336
- snapshot = formatter;
337
- }
338
- }
289
+ if (!this.#page) {
290
+ throw new Error('Response must have a page');
339
291
  }
340
- let detailedNetworkRequest;
341
- if (this.#attachedNetworkRequestId) {
342
- if (!this.#page) {
343
- throw new Error(`Response must have an McpPage`);
344
- }
345
- const request = context.getNetworkRequestById(this.#page, this.#attachedNetworkRequestId);
346
- const formatter = await NetworkFormatter.from(request, {
347
- requestId: this.#attachedNetworkRequestId,
348
- requestIdResolver: req => context.getNetworkRequestStableId(req),
349
- fetchData: true,
350
- requestFilePath: this.#attachedNetworkRequestOptions?.requestFilePath,
351
- responseFilePath: this.#attachedNetworkRequestOptions?.responseFilePath,
352
- saveFile: (data, filename, extension) => context.saveFile(data, filename, extension),
353
- redactNetworkHeaders: this.#redactNetworkHeaders,
292
+ this.#page.textSnapshot = await TextSnapshot.create(this.#page, {
293
+ verbose: this.#snapshotParams.verbose,
294
+ devtoolsData: this.#devToolsData,
295
+ });
296
+ const formatter = new SnapshotFormatter(this.#page.textSnapshot);
297
+ if (this.#snapshotParams.filePath) {
298
+ const result = await context.saveFile(new TextEncoder().encode(formatter.toString()), this.#snapshotParams.filePath, '.txt');
299
+ return result.filename;
300
+ }
301
+ else {
302
+ return formatter;
303
+ }
304
+ }
305
+ async #handleAttachedNetworkRequest(context) {
306
+ if (!this.#attachedNetworkRequestId) {
307
+ return undefined;
308
+ }
309
+ if (!this.#page) {
310
+ throw new Error(`Response must have an McpPage`);
311
+ }
312
+ const request = this.#page.getNetworkRequestById(this.#attachedNetworkRequestId);
313
+ return await NetworkFormatter.from(request, {
314
+ requestId: this.#attachedNetworkRequestId,
315
+ requestIdResolver: req => this.getNetworkRequestStableId(req),
316
+ fetchData: true,
317
+ requestFilePath: this.#attachedNetworkRequestOptions?.requestFilePath,
318
+ responseFilePath: this.#attachedNetworkRequestOptions?.responseFilePath,
319
+ saveFile: (data, filename, extension) => context.saveFile(data, filename, extension),
320
+ redactNetworkHeaders: this.#redactNetworkHeaders,
321
+ });
322
+ }
323
+ async #handleAttachedConsoleMessage() {
324
+ if (!this.#attachedConsoleMessageId) {
325
+ return undefined;
326
+ }
327
+ if (!this.#page) {
328
+ throw new Error(`Response must have an McpPage`);
329
+ }
330
+ const message = this.#page.getConsoleMessageById(this.#attachedConsoleMessageId);
331
+ const consoleMessageStableId = this.#attachedConsoleMessageId;
332
+ if ('args' in message || message instanceof UncaughtError) {
333
+ const consoleMessage = message;
334
+ return await ConsoleFormatter.from(consoleMessage, {
335
+ id: consoleMessageStableId,
336
+ fetchDetailedData: true,
337
+ devTools: this.#page.devtoolsUniverse,
354
338
  });
355
- detailedNetworkRequest = formatter;
356
339
  }
357
- let detailedConsoleMessage;
358
- if (this.#attachedConsoleMessageId) {
359
- if (!this.#page) {
360
- throw new Error(`Response must have an McpPage`);
361
- }
362
- const message = context.getConsoleMessageById(this.#page, this.#attachedConsoleMessageId);
363
- const consoleMessageStableId = this.#attachedConsoleMessageId;
364
- if ('args' in message || message instanceof UncaughtError) {
365
- const consoleMessage = message;
366
- const devTools = context.getDevToolsUniverse(this.#page);
367
- detailedConsoleMessage = await ConsoleFormatter.from(consoleMessage, {
368
- id: consoleMessageStableId,
369
- fetchDetailedData: true,
370
- devTools: devTools ?? undefined,
371
- });
372
- }
373
- else if (message instanceof DevTools.AggregatedIssue) {
374
- const formatter = new IssueFormatter(message, {
375
- id: consoleMessageStableId,
376
- requestIdResolver: context.resolveCdpRequestId.bind(context, this.#page),
377
- elementIdResolver: this.#page.resolveCdpElementId.bind(this.#page),
378
- });
379
- if (!formatter.isValid()) {
380
- throw new Error("Can't provide details for the msgid " + consoleMessageStableId);
381
- }
382
- detailedConsoleMessage = formatter;
340
+ else if (message instanceof DevTools.AggregatedIssue) {
341
+ const formatter = new IssueFormatter(message, {
342
+ id: consoleMessageStableId,
343
+ requestIdResolver: this.#page.resolveCdpRequestId.bind(this.#page),
344
+ elementIdResolver: this.#page.textSnapshot?.resolveCdpElementId.bind(this.#page.textSnapshot),
345
+ });
346
+ if (!formatter.isValid()) {
347
+ throw new Error("Can't provide details for the msgid " + consoleMessageStableId);
383
348
  }
349
+ return formatter;
384
350
  }
385
- let extensions;
386
- if (this.#listExtensions) {
387
- extensions = await context.listExtensions();
351
+ else {
352
+ return undefined;
388
353
  }
389
- // Null indicates no tools.
390
- let thirdPartyDeveloperTools;
354
+ }
355
+ async #handleThirdPartyDevelopeTools() {
391
356
  if (this.#args.categoryExperimentalThirdParty &&
392
357
  this.#listThirdPartyDeveloperTools &&
393
358
  this.#page) {
394
- thirdPartyDeveloperTools = await getToolGroup(this.#page);
395
- if (thirdPartyDeveloperTools) {
396
- this.#page.thirdPartyDeveloperTools = thirdPartyDeveloperTools;
397
- }
359
+ return await this.#page.getToolGroups();
398
360
  }
399
- let webmcpTools;
361
+ return undefined;
362
+ }
363
+ async #handleWebMCP() {
400
364
  if (this.#args.categoryExperimentalWebmcp &&
401
365
  this.#listWebMcpTools &&
402
366
  this.#page) {
403
- webmcpTools = this.#page.getWebMcpTools();
367
+ return this.#page.getWebMcpTools();
404
368
  }
405
- let consoleMessages;
406
- if (this.#consoleDataOptions?.include) {
407
- if (!this.#page) {
369
+ return undefined;
370
+ }
371
+ async #handleConsoleList(context) {
372
+ if (!this.#consoleDataOptions?.include) {
373
+ return undefined;
374
+ }
375
+ let messages;
376
+ let page;
377
+ if (this.#consoleDataOptions.serviceWorkerId) {
378
+ messages = context.getServiceWorkerConsoleData(this.#consoleDataOptions.serviceWorkerId);
379
+ }
380
+ else {
381
+ page = this.#page;
382
+ if (!page) {
408
383
  throw new Error(`Response must have an McpPage`);
409
384
  }
410
- const page = this.#page;
411
- let messages = context.getConsoleData(this.#page, this.#consoleDataOptions.includePreservedMessages);
412
- if (this.#consoleDataOptions.types?.length) {
413
- const normalizedTypes = new Set(this.#consoleDataOptions.types);
414
- messages = messages.filter(message => {
415
- if ('type' in message) {
416
- return normalizedTypes.has(message.type());
417
- }
418
- if (message instanceof DevTools.AggregatedIssue) {
419
- return normalizedTypes.has('issue');
420
- }
421
- return normalizedTypes.has('error');
422
- });
423
- }
424
- consoleMessages = (await Promise.all(messages.map(async (item) => {
425
- const consoleMessageStableId = context.getConsoleMessageStableId(item);
426
- if ('args' in item || item instanceof UncaughtError) {
427
- const consoleMessage = item;
428
- const devTools = context.getDevToolsUniverse(page);
429
- return await ConsoleFormatter.from(consoleMessage, {
430
- id: consoleMessageStableId,
431
- fetchDetailedData: false,
432
- devTools: devTools ?? undefined,
433
- });
385
+ messages = page.getConsoleData(this.#consoleDataOptions.includePreservedMessages);
386
+ }
387
+ if (this.#consoleDataOptions.types?.length) {
388
+ const normalizedTypes = new Set(this.#consoleDataOptions.types);
389
+ messages = messages.filter(message => {
390
+ if ('type' in message) {
391
+ return normalizedTypes.has(message.type());
434
392
  }
435
- if (item instanceof DevTools.AggregatedIssue) {
436
- const formatter = new IssueFormatter(item, {
437
- id: consoleMessageStableId,
438
- });
439
- if (!formatter.isValid()) {
440
- return null;
441
- }
442
- return formatter;
393
+ if (message instanceof DevTools.AggregatedIssue) {
394
+ return normalizedTypes.has('issue');
443
395
  }
444
- return null;
445
- }))).filter(item => item !== null);
396
+ return normalizedTypes.has('error');
397
+ });
446
398
  }
447
- let networkRequests;
448
- if (this.#networkRequestsOptions?.include) {
449
- if (!this.#page) {
450
- throw new Error(`Response must have an McpPage`);
451
- }
452
- let requests = context.getNetworkRequests(this.#page, this.#networkRequestsOptions?.includePreservedRequests);
453
- // Apply resource type filtering if specified
454
- if (this.#networkRequestsOptions.resourceTypes?.length) {
455
- const normalizedTypes = new Set(this.#networkRequestsOptions.resourceTypes);
456
- requests = requests.filter(request => {
457
- const type = request.resourceType();
458
- return normalizedTypes.has(type);
399
+ return (await Promise.all(messages.map(async (item) => {
400
+ const consoleMessageStableId = this.getConsoleMessageStableId(item);
401
+ if ('args' in item || item instanceof UncaughtError) {
402
+ const consoleMessage = item;
403
+ return await ConsoleFormatter.from(consoleMessage, {
404
+ id: consoleMessageStableId,
405
+ fetchDetailedData: false,
406
+ fetchStackTrace: this.#consoleDataOptions?.includeStackTraces,
407
+ devTools: page ? page.devtoolsUniverse : undefined,
459
408
  });
460
409
  }
461
- if (requests.length) {
462
- networkRequests = await Promise.all(requests.map(request => NetworkFormatter.from(request, {
463
- requestId: context.getNetworkRequestStableId(request),
464
- selectedInDevToolsUI: context.getNetworkRequestStableId(request) ===
465
- this.#networkRequestsOptions?.networkRequestIdInDevToolsUI,
466
- fetchData: false,
467
- saveFile: (data, filename, extension) => context.saveFile(data, filename, extension),
468
- redactNetworkHeaders: this.#redactNetworkHeaders,
469
- })));
410
+ if (item instanceof DevTools.AggregatedIssue) {
411
+ const formatter = new IssueFormatter(item, {
412
+ id: consoleMessageStableId,
413
+ });
414
+ if (!formatter.isValid()) {
415
+ return null;
416
+ }
417
+ return formatter;
470
418
  }
419
+ return null;
420
+ }))).filter(item => item !== null);
421
+ }
422
+ async #handleNetworkRequestList(context) {
423
+ if (!this.#networkRequestsOptions?.include) {
424
+ return undefined;
425
+ }
426
+ if (!this.#page) {
427
+ throw new Error(`Response must have an McpPage`);
428
+ }
429
+ let requests = this.#page.getNetworkRequests(this.#networkRequestsOptions?.includePreservedRequests);
430
+ // Apply resource type filtering if specified
431
+ if (this.#networkRequestsOptions.resourceTypes?.length) {
432
+ const normalizedTypes = new Set(this.#networkRequestsOptions.resourceTypes);
433
+ requests = requests.filter(request => {
434
+ const type = request.resourceType();
435
+ return normalizedTypes.has(type);
436
+ });
471
437
  }
472
- return this.format(toolName, context, {
438
+ return await Promise.all(requests.map(request => NetworkFormatter.from(request, {
439
+ requestId: this.getNetworkRequestStableId(request),
440
+ selectedInDevToolsUI: this.getNetworkRequestStableId(request) ===
441
+ this.#networkRequestsOptions?.networkRequestIdInDevToolsUI,
442
+ fetchData: false,
443
+ saveFile: (data, filename, extension) => context.saveFile(data, filename, extension),
444
+ redactNetworkHeaders: this.#redactNetworkHeaders,
445
+ })));
446
+ }
447
+ async handle(context, dataFormat = 'default') {
448
+ const [snapshot, detailedNetworkRequest, detailedConsoleMessage, thirdPartyDeveloperTools, webmcpTools, consoleMessages, networkRequests,] = await Promise.all([
449
+ this.#handleSnapshot(context),
450
+ this.#handleAttachedNetworkRequest(context),
451
+ this.#handleAttachedConsoleMessage(),
452
+ this.#handleThirdPartyDevelopeTools(),
453
+ this.#handleWebMCP(),
454
+ this.#handleConsoleList(context),
455
+ this.#handleNetworkRequestList(context),
456
+ ]);
457
+ if (this.#includeExtensionServiceWorkers) {
458
+ await context.createExtensionServiceWorkersSnapshot();
459
+ }
460
+ let extensions;
461
+ if (this.#listExtensions) {
462
+ extensions = await context.listExtensions();
463
+ }
464
+ return this.format(context, {
473
465
  detailedConsoleMessage,
474
466
  consoleMessages,
475
467
  snapshot,
@@ -482,15 +474,56 @@ export class McpResponse {
482
474
  thirdPartyDeveloperTools,
483
475
  webmcpTools,
484
476
  errorMessage: this.#error?.message,
485
- });
477
+ }, dataFormat);
486
478
  }
487
- format(toolName, context, data) {
479
+ getConsoleMessageStableId(message) {
480
+ return message[stableIdSymbol] ?? -1;
481
+ }
482
+ getNetworkRequestStableId(request) {
483
+ return request[stableIdSymbol] ?? -1;
484
+ }
485
+ async format(context, data, dataFormat = 'default') {
488
486
  const structuredContent = {};
487
+ // Resolve the compact encoder based on the chosen format
488
+ let compactEncode;
489
+ if (dataFormat === 'toon') {
490
+ try {
491
+ compactEncode = await getToonEncode();
492
+ }
493
+ catch {
494
+ throw new Error('The `@toon-format/toon` package is required to use --experimentalDataFormat=toon. ' +
495
+ 'Make sure the peer dependency is installed:\n' +
496
+ '- For npx: npx --package chrome-devtools-mcp@latest --package @toon-format/toon@latest chrome-devtools-mcp --experimentalDataFormat=toon\n' +
497
+ '- For npm: npm install @toon-format/toon (add -g if installed globally)');
498
+ }
499
+ }
500
+ else if (dataFormat === 'gcf') {
501
+ try {
502
+ compactEncode = await getGcfEncode();
503
+ }
504
+ catch {
505
+ throw new Error('The `@blackwell-systems/gcf` package is required to use --experimentalDataFormat=gcf. ' +
506
+ 'Make sure the peer dependency is installed:\n' +
507
+ '- For npx: npx --package chrome-devtools-mcp@latest --package @blackwell-systems/gcf@latest chrome-devtools-mcp --experimentalDataFormat=gcf\n' +
508
+ '- For npm: npm install @blackwell-systems/gcf (add -g if installed globally)');
509
+ }
510
+ }
489
511
  const response = [];
512
+ if (this.#reconnectNotice) {
513
+ structuredContent.reconnected = true;
514
+ response.push(`Note: the browser was restarted or reconnected since the last call. Page ids have changed. Call ${listPages().name} to see open pages.`);
515
+ }
490
516
  if (this.#textResponseLines.length) {
491
517
  structuredContent.message = this.#textResponseLines.join('\n');
492
518
  response.push(...this.#textResponseLines);
493
519
  }
520
+ if (this.#attachedWaitForResult) {
521
+ if (this.#attachedWaitForResult.navigatedToUrl) {
522
+ response.push(`Page navigated to ${this.#attachedWaitForResult.navigatedToUrl}.`);
523
+ structuredContent.navigatedToUrl =
524
+ this.#attachedWaitForResult.navigatedToUrl;
525
+ }
526
+ }
494
527
  const networkConditions = this.#page?.networkConditions;
495
528
  if (networkConditions) {
496
529
  const timeout = this.#page.pptrPage.getDefaultNavigationTimeout();
@@ -499,6 +532,11 @@ export class McpResponse {
499
532
  structuredContent.networkConditions = networkConditions;
500
533
  structuredContent.navigationTimeout = timeout;
501
534
  }
535
+ const geolocation = this.#page?.geolocation;
536
+ if (geolocation) {
537
+ response.push(`Emulating geolocation: latitude=${geolocation.latitude}, longitude=${geolocation.longitude}`);
538
+ structuredContent.geolocation = geolocation;
539
+ }
502
540
  const viewport = this.#page?.viewport;
503
541
  if (viewport) {
504
542
  response.push(`Emulating viewport: ${JSON.stringify(viewport)}`);
@@ -535,25 +573,40 @@ Call ${handleDialog.name} to handle it before continuing.`);
535
573
  }
536
574
  if (this.#includePages) {
537
575
  const allPages = context.getPages();
538
- const { regularPages, extensionPages } = allPages.reduce((acc, page) => {
539
- if (page.url().startsWith('chrome-extension://')) {
540
- acc.extensionPages.push(page);
576
+ const { regularPages, extensionPages } = allPages.reduce((acc, mcpPage) => {
577
+ if (mcpPage.pptrPage.url().startsWith('chrome-extension://')) {
578
+ acc.extensionPages.push(mcpPage);
541
579
  }
542
580
  else {
543
- acc.regularPages.push(page);
581
+ acc.regularPages.push(mcpPage);
544
582
  }
545
583
  return acc;
546
584
  }, { regularPages: [], extensionPages: [] });
585
+ const selectionFallback = context.getSelectedPageFallback();
586
+ if (selectionFallback) {
587
+ let selectedPageId;
588
+ try {
589
+ selectedPageId = context.getSelectedMcpPage().id;
590
+ }
591
+ catch {
592
+ selectedPageId = undefined;
593
+ }
594
+ response.push(`Note: the previously selected page ${selectionFallback.wasClosed ? 'was closed' : 'is no longer listed'}.${selectedPageId !== undefined ? ` Page ${selectedPageId} is now selected.` : ''}`);
595
+ }
547
596
  if (regularPages.length) {
548
597
  const parts = [`## Pages`];
549
598
  const structuredPages = [];
550
- for (const page of regularPages) {
551
- const isolatedContextName = context.getIsolatedContextName(page);
599
+ for (const mcpPage of regularPages) {
600
+ const isolatedContextName = mcpPage.isolatedContextName;
552
601
  const contextLabel = isolatedContextName
553
602
  ? ` isolatedContext=${isolatedContextName}`
554
603
  : '';
555
- parts.push(`${context.getPageId(page)}: ${page.url()}${context.isPageSelected(page) ? ' [selected]' : ''}${contextLabel}`);
556
- structuredPages.push(createStructuredPage(page, context));
604
+ const title = await fetchPageTitle(mcpPage.pptrPage);
605
+ const pageLabel = title
606
+ ? `${truncateTitle(title)} (${mcpPage.pptrPage.url()})`
607
+ : mcpPage.pptrPage.url();
608
+ parts.push(`${mcpPage.id}: ${pageLabel}${context.isPageSelected(mcpPage) ? ' [selected]' : ''}${contextLabel}`);
609
+ structuredPages.push(createStructuredPage(mcpPage, context, title));
557
610
  }
558
611
  response.push(...parts);
559
612
  structuredContent.pages = structuredPages;
@@ -562,13 +615,17 @@ Call ${handleDialog.name} to handle it before continuing.`);
562
615
  if (extensionPages.length) {
563
616
  response.push(`## Extension Pages`);
564
617
  const structuredExtensionPages = [];
565
- for (const page of extensionPages) {
566
- const isolatedContextName = context.getIsolatedContextName(page);
618
+ for (const mcpPage of extensionPages) {
619
+ const isolatedContextName = mcpPage.isolatedContextName;
567
620
  const contextLabel = isolatedContextName
568
621
  ? ` isolatedContext=${isolatedContextName}`
569
622
  : '';
570
- response.push(`${context.getPageId(page)}: ${page.url()}${context.isPageSelected(page) ? ' [selected]' : ''}${contextLabel}`);
571
- structuredExtensionPages.push(createStructuredPage(page, context));
623
+ const title = await fetchPageTitle(mcpPage.pptrPage);
624
+ const pageLabel = title
625
+ ? `${truncateTitle(title)} (${mcpPage.pptrPage.url()})`
626
+ : mcpPage.pptrPage.url();
627
+ response.push(`${mcpPage.id}: ${pageLabel}${context.isPageSelected(mcpPage) ? ' [selected]' : ''}${contextLabel}`);
628
+ structuredExtensionPages.push(createStructuredPage(mcpPage, context, title));
572
629
  }
573
630
  structuredContent.extensionPages = structuredExtensionPages;
574
631
  }
@@ -594,7 +651,7 @@ Call ${handleDialog.name} to handle it before continuing.`);
594
651
  structuredContent.tabId = this.#tabId;
595
652
  }
596
653
  if (data.traceSummary) {
597
- const summary = getTraceSummary(data.traceSummary);
654
+ const summary = getTraceSummary(data.traceSummary, this.#deviceScope);
598
655
  response.push(summary);
599
656
  structuredContent.traceSummary = summary;
600
657
  structuredContent.traceInsights = [];
@@ -602,13 +659,17 @@ Call ${handleDialog.name} to handle it before continuing.`);
602
659
  for (const [insightName, model] of Object.entries(insightSet.model)) {
603
660
  structuredContent.traceInsights.push({
604
661
  insightName,
605
- insightKey: model.insightKey,
662
+ insightKey: typeof model === 'object' &&
663
+ model !== null &&
664
+ 'insightKey' in model
665
+ ? model.insightKey
666
+ : undefined,
606
667
  });
607
668
  }
608
669
  }
609
670
  }
610
671
  if (data.traceInsight) {
611
- const insightOutput = getInsightOutput(data.traceInsight.trace, data.traceInsight.insightSetId, data.traceInsight.insightName);
672
+ const insightOutput = getInsightOutput(data.traceInsight.trace, data.traceInsight.insightSetId, data.traceInsight.insightName, this.#deviceScope);
612
673
  if ('error' in insightOutput) {
613
674
  response.push(insightOutput.error);
614
675
  }
@@ -642,9 +703,11 @@ Call ${handleDialog.name} to handle it before continuing.`);
642
703
  structuredContent.snapshotFilePath = data.snapshot;
643
704
  }
644
705
  else {
645
- response.push('## Latest page snapshot');
646
- response.push(data.snapshot.toString());
647
706
  structuredContent.snapshot = data.snapshot.toJSON();
707
+ response.push('## Latest page snapshot');
708
+ response.push(compactEncode
709
+ ? compactEncode(structuredContent.snapshot)
710
+ : data.snapshot.toString());
648
711
  }
649
712
  }
650
713
  if (this.#heapSnapshotOptions?.include) {
@@ -661,28 +724,125 @@ Call ${handleDialog.name} to handle it before continuing.`);
661
724
  structuredContent.heapSnapshot = structuredContent.heapSnapshot || {};
662
725
  structuredContent.heapSnapshot.staticData = staticData;
663
726
  }
664
- const aggregates = this.#heapSnapshotOptions.aggregates;
665
- if (aggregates) {
666
- const sortedEntries = HeapSnapshotFormatter.sort(aggregates);
727
+ const nativeContextSizes = this.#heapSnapshotOptions.nativeContextSizes;
728
+ if (nativeContextSizes) {
729
+ response.push('### Native Contexts');
730
+ response.push(HeapSnapshotFormatter.formatNativeContextSizes(nativeContextSizes));
731
+ structuredContent.heapSnapshot = structuredContent.heapSnapshot || {};
732
+ structuredContent.heapSnapshot.nativeContextSizes = nativeContextSizes;
733
+ }
734
+ const retainedByContextSummary = this.#heapSnapshotOptions.retainedByContextSummary;
735
+ if (retainedByContextSummary) {
736
+ response.push('### Retained by Context Summary');
737
+ response.push(HeapSnapshotFormatter.formatRetainedByContextSummary(retainedByContextSummary));
738
+ structuredContent.heapSnapshot = structuredContent.heapSnapshot || {};
739
+ structuredContent.heapSnapshot.retainedByContextSummary =
740
+ retainedByContextSummary;
741
+ }
742
+ const aggregateData = this.#heapSnapshotOptions.aggregateData;
743
+ if (aggregateData) {
744
+ const sortedEntries = HeapSnapshotFormatter.sort(aggregateData.aggregates);
667
745
  const paginationData = this.#dataWithPagination(sortedEntries, this.#heapSnapshotOptions.pagination);
746
+ response.push(`Objects: ${aggregateData.objectCount}`);
747
+ response.push(`Total shallow size: ${formatBytesToKb(aggregateData.totalSelfSize)}`);
748
+ structuredContent.heapSnapshot = structuredContent.heapSnapshot || {};
749
+ structuredContent.heapSnapshot.aggregateStats = {
750
+ objectCount: aggregateData.objectCount,
751
+ totalSelfSize: aggregateData.totalSelfSize,
752
+ };
668
753
  structuredContent.pagination = paginationData.pagination;
669
754
  response.push(...paginationData.info);
670
755
  const paginatedRecord = Object.fromEntries(paginationData.items);
671
756
  const formatter = new HeapSnapshotFormatter(paginatedRecord);
672
- response.push(formatter.toString());
673
757
  structuredContent.heapSnapshotData = formatter.toJSON();
758
+ response.push(compactEncode
759
+ ? compactEncode(structuredContent.heapSnapshotData)
760
+ : formatter.toString());
674
761
  }
675
762
  const nodes = this.#heapSnapshotOptions.nodes;
676
763
  if (nodes) {
677
- const sortedItems = nodes.items
678
- .filter(isNodeLike)
679
- .sort((a, b) => b.retainedSize - a.retainedSize);
680
- const paginationData = this.#dataWithPagination(sortedItems, this.#heapSnapshotOptions.pagination);
764
+ let items = Array.from(nodes.items);
765
+ const firstItem = nodes.items[0];
766
+ if (firstItem) {
767
+ if (isNodeLike(firstItem)) {
768
+ items = items
769
+ .filter(isNodeLike)
770
+ .sort((a, b) => b.retainedSize - a.retainedSize);
771
+ }
772
+ else if (isEdgeLike(firstItem)) {
773
+ items = items.filter(isEdgeLike);
774
+ }
775
+ }
776
+ const paginationData = this.#dataWithPagination(items, this.#heapSnapshotOptions.pagination);
681
777
  response.push(HeapSnapshotFormatter.formatNodes(paginationData.items));
682
778
  structuredContent.pagination = paginationData.pagination;
683
779
  response.push(...paginationData.info);
684
780
  structuredContent.heapSnapshotNodes = paginationData.items;
685
781
  }
782
+ const retainingPaths = this.#heapSnapshotOptions.retainingPaths;
783
+ if (retainingPaths) {
784
+ response.push('### Retaining Paths');
785
+ const { paths, limitsReached } = retainingPaths;
786
+ if (paths.length === 0) {
787
+ response.push('No retaining paths found.');
788
+ }
789
+ else {
790
+ response.push(HeapSnapshotFormatter.formatRetainingPaths(paths));
791
+ }
792
+ const reached = Object.entries(limitsReached)
793
+ .filter(([, hit]) => hit)
794
+ .map(([limit]) => limit);
795
+ if (reached.length > 0) {
796
+ response.push(`Note: results are truncated, the following limits were reached: ${reached.join(', ')}.`);
797
+ }
798
+ structuredContent.heapSnapshotRetainingPaths =
799
+ retainingPaths;
800
+ }
801
+ const dominators = this.#heapSnapshotOptions.dominators;
802
+ if (dominators) {
803
+ response.push('### Dominator Chain');
804
+ if (dominators.length === 0) {
805
+ response.push('No dominators found.');
806
+ }
807
+ else {
808
+ response.push(HeapSnapshotFormatter.formatDominators(dominators));
809
+ }
810
+ structuredContent.heapSnapshotDominators = dominators;
811
+ }
812
+ const classDiffs = this.#heapSnapshotOptions.classDiffs;
813
+ if (classDiffs) {
814
+ response.push('### Heap Snapshot Diff');
815
+ response.push(compactEncode
816
+ ? compactEncode(classDiffs)
817
+ : HeapSnapshotFormatter.formatDiffSummary(classDiffs));
818
+ structuredContent.heapSnapshotClassDiffs = classDiffs;
819
+ }
820
+ const detailedClassDiff = this.#heapSnapshotOptions.detailedClassDiff;
821
+ if (detailedClassDiff) {
822
+ response.push('### Heap Snapshot Detailed Diff');
823
+ response.push(compactEncode
824
+ ? compactEncode(detailedClassDiff)
825
+ : HeapSnapshotFormatter.formatDiffDetails(detailedClassDiff));
826
+ structuredContent.heapSnapshotDetailedClassDiff = detailedClassDiff;
827
+ }
828
+ const duplicateStrings = this.#heapSnapshotOptions.duplicateStrings;
829
+ if (duplicateStrings) {
830
+ response.push('### Duplicate Strings');
831
+ const paginationData = this.#dataWithPagination(duplicateStrings, this.#heapSnapshotOptions.pagination);
832
+ structuredContent.pagination = paginationData.pagination;
833
+ response.push(...paginationData.info);
834
+ const formatted = HeapSnapshotFormatter.formatDuplicateStrings(paginationData.items);
835
+ response.push(formatted);
836
+ structuredContent.heapSnapshotDuplicateStrings = paginationData.items;
837
+ }
838
+ const objectInfo = this.#heapSnapshotOptions.objectInfo;
839
+ if (objectInfo) {
840
+ response.push('### Object Details');
841
+ response.push(compactEncode
842
+ ? compactEncode(objectInfo)
843
+ : HeapSnapshotFormatter.formatObjectInfo(objectInfo));
844
+ structuredContent.heapSnapshotObjectDetails = objectInfo;
845
+ }
686
846
  }
687
847
  if (data.detailedNetworkRequest) {
688
848
  response.push(data.detailedNetworkRequest.toStringDetailed());
@@ -710,18 +870,11 @@ Call ${handleDialog.name} to handle it before continuing.`);
710
870
  response.push(extensionsMessage);
711
871
  }
712
872
  }
713
- if (data.thirdPartyDeveloperTools !== undefined) {
714
- if (data.thirdPartyDeveloperTools) {
715
- structuredContent.thirdPartyDeveloperTools =
716
- data.thirdPartyDeveloperTools;
717
- }
873
+ const thirdPartyDeveloperTools = data.thirdPartyDeveloperTools;
874
+ if (thirdPartyDeveloperTools?.length) {
875
+ structuredContent.thirdPartyDeveloperTools = thirdPartyDeveloperTools;
718
876
  response.push('## Third-party developer tools');
719
- if (data.thirdPartyDeveloperTools === null ||
720
- !data.thirdPartyDeveloperTools?.tools) {
721
- response.push('No third-party developer tools available.');
722
- }
723
- else {
724
- const toolGroup = data.thirdPartyDeveloperTools;
877
+ for (const toolGroup of thirdPartyDeveloperTools) {
725
878
  response.push(`${toolGroup.name}: ${toolGroup.description}`);
726
879
  response.push('Available tools:');
727
880
  const toolDefinitionsMessage = toolGroup.tools
@@ -760,11 +913,10 @@ Call ${handleDialog.name} to handle it before continuing.`);
760
913
  structuredContent.pagination = paginationData.pagination;
761
914
  response.push(...paginationData.info);
762
915
  if (data.networkRequests) {
763
- structuredContent.networkRequests = [];
764
- for (const formatter of paginationData.items) {
765
- response.push(formatter.toString());
766
- structuredContent.networkRequests.push(formatter.toJSON());
767
- }
916
+ structuredContent.networkRequests = paginationData.items.map(i => i.toJSON());
917
+ response.push(...(compactEncode
918
+ ? [compactEncode(structuredContent.networkRequests)]
919
+ : paginationData.items.map(i => i.toString())));
768
920
  }
769
921
  }
770
922
  else {
@@ -778,9 +930,17 @@ Call ${handleDialog.name} to handle it before continuing.`);
778
930
  const grouped = ConsoleFormatter.groupConsecutive(messages);
779
931
  const paginationData = this.#dataWithPagination(grouped, this.#consoleDataOptions.pagination);
780
932
  structuredContent.pagination = paginationData.pagination;
781
- response.push(...paginationData.info);
782
- response.push(...paginationData.items.map(item => item.toString()));
783
933
  structuredContent.consoleMessages = paginationData.items.map(item => item.toJSON());
934
+ response.push(...paginationData.info);
935
+ if (compactEncode) {
936
+ response.push(compactEncode(structuredContent.consoleMessages));
937
+ }
938
+ else {
939
+ response.push(...paginationData.items.map(item => item.toString()));
940
+ }
941
+ if (structuredContent.consoleMessages.some(message => 'stackTrace' in message)) {
942
+ response.push('Note: stack trace line and column numbers use 1-based indexing');
943
+ }
784
944
  }
785
945
  else {
786
946
  response.push('<no console messages found>');
@@ -839,12 +999,26 @@ Call ${handleDialog.name} to handle it before continuing.`);
839
999
  this.#textResponseLines = [];
840
1000
  }
841
1001
  }
842
- function createStructuredPage(page, context) {
843
- const isolatedContextName = context.getIsolatedContextName(page);
1002
+ function truncateTitle(title, maxLength = 50) {
1003
+ if (title.length <= maxLength) {
1004
+ return title;
1005
+ }
1006
+ return title.slice(0, maxLength - 3) + '...';
1007
+ }
1008
+ async function fetchPageTitle(page) {
1009
+ return Promise.race([
1010
+ page.title().catch(() => ''),
1011
+ new Promise(resolve => setTimeout(() => resolve(''), 1000)),
1012
+ ]);
1013
+ }
1014
+ function createStructuredPage(mcpPage, context, rawTitle) {
1015
+ const isolatedContextName = mcpPage.isolatedContextName;
1016
+ const title = truncateTitle(rawTitle);
844
1017
  const entry = {
845
- id: context.getPageId(page),
846
- url: page.url(),
847
- selected: context.isPageSelected(page),
1018
+ id: mcpPage.id,
1019
+ url: mcpPage.pptrPage.url(),
1020
+ title,
1021
+ selected: context.isPageSelected(mcpPage),
848
1022
  };
849
1023
  if (isolatedContextName) {
850
1024
  entry.isolatedContext = isolatedContextName;