opera-devtools-mcp 0.2.4 → 0.4.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 +108 -8
  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 +614 -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 +116 -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 +193 -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 +7 -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 -4
  293. package/package.json +43 -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
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { McpResponse } from './McpResponse.js';
7
7
  export class SlimMcpResponse extends McpResponse {
8
- async handle(_toolName, _context) {
8
+ async handle(_context) {
9
9
  const text = {
10
10
  type: 'text',
11
11
  text: this.responseLines.join('\n'),
@@ -3,7 +3,59 @@
3
3
  * Copyright 2025 Google LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
- import { logger } from './logger.js';
6
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
7
+ if (value !== null && value !== void 0) {
8
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
9
+ var dispose, inner;
10
+ if (async) {
11
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
12
+ dispose = value[Symbol.asyncDispose];
13
+ }
14
+ if (dispose === void 0) {
15
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
16
+ dispose = value[Symbol.dispose];
17
+ if (async) inner = dispose;
18
+ }
19
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
20
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
21
+ env.stack.push({ value: value, dispose: dispose, async: async });
22
+ }
23
+ else if (async) {
24
+ env.stack.push({ async: true });
25
+ }
26
+ return value;
27
+ };
28
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
29
+ return function (env) {
30
+ function fail(e) {
31
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
32
+ env.hasError = true;
33
+ }
34
+ var r, s = 0;
35
+ function next() {
36
+ while (r = env.stack.pop()) {
37
+ try {
38
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
39
+ if (r.dispose) {
40
+ var result = r.dispose.call(r.value);
41
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
42
+ }
43
+ else s |= 1;
44
+ }
45
+ catch (e) {
46
+ fail(e);
47
+ }
48
+ }
49
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
50
+ if (env.hasError) throw env.error;
51
+ }
52
+ return next();
53
+ };
54
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
55
+ var e = new Error(message);
56
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
57
+ });
58
+ import { logger } from './utils/logger.js';
7
59
  export class TextSnapshot {
8
60
  static nextSnapshotId = 1;
9
61
  static resetCounter() {
@@ -88,7 +140,7 @@ export class TextSnapshot {
88
140
  const data = options.devtoolsData ?? (await page.getDevToolsData());
89
141
  if (data?.cdpBackendNodeId) {
90
142
  snapshot.hasSelectedElement = true;
91
- snapshot.selectedElementUid = page.resolveCdpElementId(data.cdpBackendNodeId);
143
+ snapshot.selectedElementUid = snapshot.resolveCdpElementId(data.cdpBackendNodeId);
92
144
  }
93
145
  // Clean up unique IDs that we did not see anymore.
94
146
  for (const key of uniqueBackendNodeIdToMcpId.keys()) {
@@ -98,6 +150,24 @@ export class TextSnapshot {
98
150
  }
99
151
  return snapshot;
100
152
  }
153
+ resolveCdpElementId(cdpBackendNodeId) {
154
+ if (!cdpBackendNodeId) {
155
+ logger?.('no cdpBackendNodeId');
156
+ return;
157
+ }
158
+ // TODO: index by backendNodeId instead.
159
+ const queue = [this.root];
160
+ while (queue.length) {
161
+ const current = queue.pop();
162
+ if (current.backendNodeId === cdpBackendNodeId) {
163
+ return current.id;
164
+ }
165
+ for (const child of current.children) {
166
+ queue.push(child);
167
+ }
168
+ }
169
+ return;
170
+ }
101
171
  // ExtraHandles represent DOM nodes which might not be part of the accessibility tree, e.g. DOM nodes
102
172
  // returned by third-party developer tools. We insert them into the tree by finding the closest ancestor
103
173
  // in the tree and inserting the node as a child. The ancestor's child nodes are re-parented if necessary.
@@ -135,28 +205,37 @@ export class TextSnapshot {
135
205
  return extraNode;
136
206
  };
137
207
  const findAncestorNode = async (handle) => {
138
- let ancestorHandle = await handle.evaluateHandle(el => el.parentElement);
139
- while (ancestorHandle) {
140
- const ancestorElement = ancestorHandle.asElement();
141
- if (!ancestorElement) {
142
- await ancestorHandle.dispose();
143
- return null;
144
- }
145
- const ancestorBackendId = await ancestorElement.backendNodeId();
146
- if (ancestorBackendId) {
147
- const ancestorNode = idToNode
148
- .values()
149
- .find(node => node.backendNodeId === ancestorBackendId);
150
- if (ancestorNode) {
151
- await ancestorHandle.dispose();
152
- return ancestorNode;
208
+ const env_1 = { stack: [], error: void 0, hasError: false };
209
+ try {
210
+ let ancestorHandle = await handle.evaluateHandle(el => el.parentElement);
211
+ const stack = __addDisposableResource(env_1, new DisposableStack(), false);
212
+ while (ancestorHandle) {
213
+ stack.use(ancestorHandle);
214
+ const ancestorElement = ancestorHandle.asElement();
215
+ if (!ancestorElement) {
216
+ return null;
153
217
  }
218
+ const ancestorBackendId = await ancestorElement.backendNodeId();
219
+ if (ancestorBackendId) {
220
+ const ancestorNode = idToNode
221
+ .values()
222
+ .find(node => node.backendNodeId === ancestorBackendId);
223
+ if (ancestorNode) {
224
+ return ancestorNode;
225
+ }
226
+ }
227
+ const nextHandle = await ancestorElement.evaluateHandle(el => el.parentElement);
228
+ ancestorHandle = nextHandle;
154
229
  }
155
- const nextHandle = await ancestorElement.evaluateHandle(el => el.parentElement);
156
- await ancestorHandle.dispose();
157
- ancestorHandle = nextHandle;
230
+ return null;
231
+ }
232
+ catch (e_1) {
233
+ env_1.error = e_1;
234
+ env_1.hasError = true;
235
+ }
236
+ finally {
237
+ __disposeResources(env_1);
158
238
  }
159
- return null;
160
239
  };
161
240
  const findDescendantNodes = async (backendNodeId) => {
162
241
  const descendantIds = new Set();
@@ -186,7 +265,7 @@ export class TextSnapshot {
186
265
  }
187
266
  }
188
267
  catch (e) {
189
- logger(`Failed to collect descendants for backend node ${backendNodeId}`, e);
268
+ logger?.(`Failed to collect descendants for backend node ${backendNodeId}`, e);
190
269
  }
191
270
  return descendantIds;
192
271
  };
@@ -2,14 +2,22 @@
2
2
  * @license
3
3
  * Copyright 2026 Google LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
+ *
6
+ * Modified by Opera Software AS: optional `hooks` seam (see
7
+ * ./opera/toolHandlerHooks.ts) for mutex bypass, browser relaunch and log
8
+ * streaming. Keep the diff to the three `this.hooks?.` call sites.
5
9
  */
6
- import { logger } from './logger.js';
7
10
  import { McpResponse } from './McpResponse.js';
11
+ import { CLI_BIN_NAME } from './opera/branding.js';
8
12
  import { SlimMcpResponse } from './SlimMcpResponse.js';
9
13
  import { ClearcutLogger } from './telemetry/ClearcutLogger.js';
10
- import { bucketizeLatency } from './telemetry/metricUtils.js';
14
+ import { bucketizeLatency, buildContext } from './telemetry/transformation.js';
15
+ import { zod } from './third_party/index.js';
11
16
  import { labels, OFF_BY_DEFAULT_CATEGORIES } from './tools/categories.js';
12
17
  import { pageIdSchema } from './tools/ToolDefinition.js';
18
+ import { logger } from './utils/logger.js';
19
+ import { fileURLToPath } from 'node:url';
20
+ import { isLocalhost } from './utils/url.js';
13
21
  export function buildFlag(category) {
14
22
  return `category${category.charAt(0).toUpperCase() + category.slice(1)}`;
15
23
  }
@@ -17,7 +25,7 @@ function buildDisabledMessage(toolName, flag, categoryLabel) {
17
25
  const reason = categoryLabel
18
26
  ? `is in category ${categoryLabel} which`
19
27
  : `requires experimental feature ${flag} and`;
20
- return `Tool ${toolName} ${reason} is currently disabled. Enable it by running chrome-devtools start ${flag}=true. For more information check the README.`;
28
+ return `Tool ${toolName} ${reason} is currently disabled. Enable it by running ${CLI_BIN_NAME} start ${flag}=true. For more information check the README.`;
21
29
  }
22
30
  function getCategoryStatus(category, serverArgs) {
23
31
  const categoryFlag = buildFlag(category);
@@ -70,19 +78,89 @@ function getToolStatusInfo(tool, serverArgs) {
70
78
  function isPageScopedTool(tool) {
71
79
  return 'pageScoped' in tool && tool.pageScoped === true;
72
80
  }
81
+ function formatArgumentNames(names) {
82
+ return names.map(name => `"${name}"`).join(', ');
83
+ }
84
+ function buildUnknownArgumentsMessage(toolName, unknownArgumentNames, expectedArgumentNames) {
85
+ const unknownLabel = unknownArgumentNames.length === 1 ? 'argument' : 'arguments';
86
+ const expectedArguments = expectedArgumentNames.length
87
+ ? `Expected arguments: ${formatArgumentNames(expectedArgumentNames)}.`
88
+ : 'This tool does not accept any arguments.';
89
+ const correction = unknownArgumentNames.length === 1 ? 'Remove it' : 'Remove them';
90
+ return `Unknown ${unknownLabel} for tool "${toolName}": ${formatArgumentNames(unknownArgumentNames)}. ${expectedArguments} ${correction} and retry.`;
91
+ }
92
+ function extractPaths(value) {
93
+ if (typeof value === 'string') {
94
+ return [value];
95
+ }
96
+ if (Array.isArray(value)) {
97
+ return value.filter(item => typeof item === 'string');
98
+ }
99
+ return [];
100
+ }
101
+ function isLocalBrowser(context) {
102
+ if (context.browser.process()) {
103
+ return true;
104
+ }
105
+ const wsEndpoint = context.browser.wsEndpoint();
106
+ if (wsEndpoint && isLocalhost(wsEndpoint)) {
107
+ return true;
108
+ }
109
+ return false;
110
+ }
111
+ function shouldValidateFile(option, isLocal) {
112
+ if (option === true) {
113
+ return true;
114
+ }
115
+ if (typeof option === 'object' && option !== null) {
116
+ if (isLocal) {
117
+ return Boolean(option.local);
118
+ }
119
+ return Boolean(option.remote);
120
+ }
121
+ return false;
122
+ }
123
+ async function validateToolFiles(tool, params, context) {
124
+ const isLocal = isLocalBrowser(context);
125
+ const pathsOrUrlsToValidate = [];
126
+ for (const [key, option] of Object.entries(tool.verifyFilesSchema)) {
127
+ if (shouldValidateFile(option, isLocal)) {
128
+ pathsOrUrlsToValidate.push(...extractPaths(params[key]));
129
+ }
130
+ }
131
+ for (const filePathOrUrl of pathsOrUrlsToValidate) {
132
+ let filePath = filePathOrUrl;
133
+ try {
134
+ const url = new URL(filePathOrUrl);
135
+ if (url.protocol === 'file:') {
136
+ filePath = fileURLToPath(url);
137
+ }
138
+ else if (['http:', 'https:', 'ws:', 'wss:'].includes(url.protocol)) {
139
+ continue;
140
+ }
141
+ }
142
+ catch {
143
+ // Suppress parsing errors for regular file paths.
144
+ }
145
+ await context.validatePath(filePath);
146
+ }
147
+ }
73
148
  export class ToolHandler {
74
149
  tool;
75
150
  serverArgs;
76
151
  getContext;
77
152
  toolMutex;
153
+ hooks;
78
154
  inputSchema;
155
+ registeredInputSchema;
79
156
  shouldRegister;
80
157
  disabledReason;
81
- constructor(tool, serverArgs, getContext, toolMutex) {
158
+ constructor(tool, serverArgs, getContext, toolMutex, hooks) {
82
159
  this.tool = tool;
83
160
  this.serverArgs = serverArgs;
84
161
  this.getContext = getContext;
85
162
  this.toolMutex = toolMutex;
163
+ this.hooks = hooks;
86
164
  const { disabled, reason } = getToolStatusInfo(tool, serverArgs);
87
165
  this.disabledReason = reason;
88
166
  this.shouldRegister = !(disabled && !serverArgs.viaCli);
@@ -91,10 +169,14 @@ export class ToolHandler {
91
169
  tool.pageScoped &&
92
170
  serverArgs.experimentalPageIdRouting &&
93
171
  !serverArgs.slim
94
- ? { ...tool.schema, ...pageIdSchema }
172
+ ? { ...pageIdSchema, ...tool.schema }
95
173
  : tool.schema;
174
+ this.registeredInputSchema = zod.object(this.inputSchema).passthrough();
96
175
  }
97
- async handle(params) {
176
+ unknownArgumentNames(params) {
177
+ return Object.keys(params).filter(key => !Object.hasOwn(this.inputSchema, key));
178
+ }
179
+ async handle(params, extra) {
98
180
  if (this.disabledReason) {
99
181
  return {
100
182
  content: [
@@ -106,26 +188,49 @@ export class ToolHandler {
106
188
  isError: true,
107
189
  };
108
190
  }
109
- const guard = await this.toolMutex.acquire();
191
+ const unknownArgumentNames = this.unknownArgumentNames(params);
192
+ if (unknownArgumentNames.length) {
193
+ return {
194
+ content: [
195
+ {
196
+ type: 'text',
197
+ text: buildUnknownArgumentsMessage(this.tool.name, unknownArgumentNames, Object.keys(this.inputSchema)),
198
+ },
199
+ ],
200
+ isError: true,
201
+ };
202
+ }
203
+ const guard = this.hooks?.bypassMutex(this.tool)
204
+ ? undefined
205
+ : await this.toolMutex.acquire();
110
206
  const startTime = Date.now();
111
207
  let success = false;
208
+ let devToolsData;
209
+ let pageUrl;
112
210
  try {
113
- logger(`${this.tool.name} request: ${JSON.stringify(params, null, ' ')}`);
211
+ logger?.(`${this.tool.name} request: ${JSON.stringify(params, null, ' ')}`);
212
+ await this.hooks?.beforeInvoke(this.tool);
114
213
  const context = await this.getContext();
115
- logger(`${this.tool.name} context: resolved`);
116
- await context.detectOpenDevToolsWindows();
214
+ logger?.(`${this.tool.name} context: resolved`);
215
+ const logCallback = this.hooks?.makeLogCallback(extra);
117
216
  const response = this.serverArgs.slim
118
- ? new SlimMcpResponse(this.serverArgs)
119
- : new McpResponse(this.serverArgs);
217
+ ? new SlimMcpResponse(this.serverArgs, logCallback)
218
+ : new McpResponse(this.serverArgs, logCallback);
120
219
  response.setRedactNetworkHeaders(this.serverArgs.redactNetworkHeaders);
220
+ if (context.consumeReconnectNotice()) {
221
+ response.setReconnectNotice();
222
+ }
223
+ let page;
121
224
  try {
225
+ await validateToolFiles(this.tool, params, context);
122
226
  if (isPageScopedTool(this.tool)) {
123
227
  const pageId = typeof params.pageId === 'number' ? params.pageId : undefined;
124
- const page = this.serverArgs.experimentalPageIdRouting &&
125
- pageId !== undefined &&
126
- !this.serverArgs.slim
127
- ? context.getPageById(pageId)
128
- : context.getSelectedMcpPage();
228
+ page =
229
+ this.serverArgs.experimentalPageIdRouting &&
230
+ pageId !== undefined &&
231
+ !this.serverArgs.slim
232
+ ? context.getPageById(pageId)
233
+ : context.getSelectedMcpPage();
129
234
  response.setPage(page);
130
235
  if (this.tool.blockedByDialog) {
131
236
  page.throwIfDialogOpen();
@@ -133,18 +238,33 @@ export class ToolHandler {
133
238
  await this.tool.handler({
134
239
  params,
135
240
  page,
241
+ signal: extra?.signal,
136
242
  }, response, context);
137
243
  }
138
244
  else {
139
245
  await this.tool.handler({
140
246
  params,
247
+ signal: extra?.signal,
141
248
  }, response, context);
142
249
  }
143
250
  }
144
251
  catch (err) {
145
252
  response.setError(err);
146
253
  }
147
- const { content, structuredContent } = await response.handle(this.tool.name, context);
254
+ devToolsData = await context.getDevToolsData(page);
255
+ const targetPage = page ?? context.getSelectedMcpPage();
256
+ if (targetPage?.pptrPage?.isClosed() === false) {
257
+ pageUrl = targetPage.pptrPage.url();
258
+ }
259
+ // Resolve data format: --experimentalDataFormat takes precedence, fall back to legacy --experimentalToonFormat
260
+ let dataFormat = 'default';
261
+ if (this.serverArgs.experimentalDataFormat) {
262
+ dataFormat = this.serverArgs.experimentalDataFormat;
263
+ }
264
+ else if (this.serverArgs.experimentalToonFormat) {
265
+ dataFormat = 'toon';
266
+ }
267
+ const { content, structuredContent } = await response.handle(context, dataFormat);
148
268
  const result = {
149
269
  content,
150
270
  };
@@ -158,7 +278,7 @@ export class ToolHandler {
158
278
  return result;
159
279
  }
160
280
  catch (err) {
161
- logger(`${this.tool.name} error:`, err, err?.stack);
281
+ logger?.(`${this.tool.name} error:`, err, err?.stack);
162
282
  let errorText = err && 'message' in err ? err.message : String(err);
163
283
  if ('cause' in err && err.cause) {
164
284
  errorText += `\nCause: ${err.cause.message}`;
@@ -174,14 +294,16 @@ export class ToolHandler {
174
294
  };
175
295
  }
176
296
  finally {
297
+ const context = buildContext(devToolsData, pageUrl);
177
298
  void ClearcutLogger.get()?.logToolInvocation({
178
299
  toolName: this.tool.name,
179
300
  params,
180
301
  schema: this.inputSchema,
181
302
  success,
182
303
  latencyMs: bucketizeLatency(Date.now() - startTime),
304
+ context,
183
305
  });
184
- guard.dispose();
306
+ guard?.[Symbol.dispose]();
185
307
  }
186
308
  }
187
309
  }
@@ -5,13 +5,39 @@
5
5
  *
6
6
  * Modified by Opera Software AS.
7
7
  */
8
+ import { execSync } from 'node:child_process';
8
9
  import fs from 'node:fs/promises';
9
10
  import path from 'node:path';
10
11
  import process from 'node:process';
12
+ import { PACKAGE_NAME } from '../opera/branding.js';
13
+ const DEFAULT_REGISTRY = 'https://registry.npmjs.org';
14
+ function getRegistry() {
15
+ // Use the user's configured npm registry so update checks work behind
16
+ // corporate proxies and private registries. `npm config get registry`
17
+ // honors .npmrc files at every scope and respects npm_config_registry,
18
+ // so it covers direct CLI invocations as well as `npx` / `npm run`.
19
+ try {
20
+ const registry = execSync('npm config get registry', {
21
+ encoding: 'utf8',
22
+ stdio: ['ignore', 'pipe', 'ignore'],
23
+ timeout: 5000,
24
+ windowsHide: true,
25
+ })
26
+ .trim()
27
+ .replace(/\/$/, '');
28
+ if (registry && registry !== 'undefined' && /^https?:\/\//.test(registry)) {
29
+ return registry;
30
+ }
31
+ }
32
+ catch {
33
+ // npm not on PATH or other errors, fall back to default.
34
+ }
35
+ return DEFAULT_REGISTRY;
36
+ }
11
37
  const cachePath = process.argv[2];
12
38
  if (cachePath) {
13
39
  try {
14
- const response = await fetch('https://registry.npmjs.org/opera-devtools-mcp/latest');
40
+ const response = await fetch(`${getRegistry()}/${PACKAGE_NAME}/latest`);
15
41
  const data = response.ok ? await response.json() : null;
16
42
  if (data &&
17
43
  typeof data === 'object' &&