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.
- package/README.md +104 -7
- package/build/src/McpContext.js +365 -365
- package/build/src/McpPage.js +475 -48
- package/build/src/McpResponse.js +462 -288
- package/build/src/SlimMcpResponse.js +1 -1
- package/build/src/TextSnapshot.js +101 -22
- package/build/src/ToolHandler.js +142 -20
- package/build/src/bin/check-latest-version.js +27 -1
- package/build/src/bin/chrome-devtools-cli-options.js +573 -10
- package/build/src/bin/chrome-devtools-mcp-cli-options.js +119 -32
- package/build/src/bin/chrome-devtools-mcp-main.js +50 -7
- package/build/src/bin/chrome-devtools-mcp.js +7 -4
- package/build/src/bin/chrome-devtools.js +57 -14
- package/build/src/bin/opera-devtools-cli-options.js +3 -829
- package/build/src/bin/opera-devtools-mcp-cli-options.js +3 -348
- package/build/src/bin/opera-devtools-mcp-main.js +3 -37
- package/build/src/bin/opera-devtools-mcp.js +4 -19
- package/build/src/bin/opera-devtools.js +4 -185
- package/build/src/browser.js +45 -5
- package/build/src/{PageCollector.js → collectors/PageCollector.js} +64 -133
- package/build/src/collectors/ServiceWorkerCollector.js +171 -0
- package/build/src/daemon/client.js +75 -17
- package/build/src/daemon/daemon.js +92 -30
- package/build/src/daemon/utils.js +24 -8
- package/build/src/{DevtoolsUtils.js → devtools/DevtoolsUtils.js} +94 -88
- package/build/src/devtools/McpHostBindingAdapter.js +165 -0
- package/build/src/{issue-descriptions.js → devtools/issueDescriptions.js} +8 -6
- package/build/src/formatters/ConsoleFormatter.js +44 -11
- package/build/src/formatters/HeapSnapshotFormatter.js +140 -9
- package/build/src/formatters/IssueFormatter.js +7 -7
- package/build/src/formatters/NetworkFormatter.js +10 -8
- package/build/src/index.js +101 -232
- package/build/src/opera/branding.js +34 -0
- package/build/src/opera/browserFlags.js +85 -0
- package/build/src/opera/browserLaunch.js +52 -0
- package/build/src/opera/policy.js +50 -0
- package/build/src/opera/serviceWorkerRetry.js +37 -0
- package/build/src/opera/toolHandlerHooks.js +39 -0
- package/build/src/{tools → opera/tools}/opera.js +9 -21
- package/build/src/processors/HeapSnapshotManager.js +258 -0
- package/build/src/{trace-processing/parse.js → processors/PerformanceTrace.js} +9 -9
- package/build/src/telemetry/ClearcutLogger.js +12 -119
- package/build/src/telemetry/WatchdogClient.js +5 -5
- package/build/src/telemetry/errors.js +1 -1
- package/build/src/telemetry/flagUtils.js +6 -6
- package/build/src/telemetry/{toolMetricsUtils.js → metricsRegistry.js} +4 -11
- package/build/src/telemetry/persistence.js +3 -3
- package/build/src/telemetry/transformation.js +184 -0
- package/build/src/telemetry/watchdog/ClearcutSender.js +11 -11
- package/build/src/telemetry/watchdog/main.js +6 -6
- package/build/src/third_party/THIRD_PARTY_NOTICES +232 -1061
- package/build/src/third_party/bundled-packages.json +6 -8
- package/build/src/third_party/devtools-formatter-worker.js +63 -22
- package/build/src/third_party/devtools-heap-snapshot-worker.js +809 -182
- package/build/src/third_party/index.js +69558 -74691
- package/build/src/third_party/issue-descriptions/CoepCorpNotSameOrigin.md +2 -2
- package/build/src/third_party/issue-descriptions/CoepCorpNotSameOriginAfterDefaultedToSameOriginByCoep.md +3 -3
- package/build/src/third_party/issue-descriptions/CoepCorpNotSameSite.md +2 -2
- package/build/src/third_party/issue-descriptions/CoepFrameResourceNeedsCoepHeader.md +1 -1
- package/build/src/third_party/issue-descriptions/CompatibilityModeQuirks.md +1 -1
- package/build/src/third_party/issue-descriptions/CookieAttributeValueExceedsMaxSize.md +2 -2
- package/build/src/third_party/issue-descriptions/LowTextContrast.md +1 -1
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorRead.md +1 -1
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureErrorSet.md +1 -1
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnRead.md +1 -1
- package/build/src/third_party/issue-descriptions/SameSiteNoneInsecureWarnSet.md +1 -1
- package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeRead.md +3 -3
- package/build/src/third_party/issue-descriptions/SameSiteUnspecifiedLaxAllowUnsafeSet.md +3 -3
- package/build/src/third_party/issue-descriptions/bounceTrackingMitigations.md +1 -1
- package/build/src/third_party/issue-descriptions/connectionAllowlistEmbeddingRequirementNotSatisfied.md +26 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistIFrameAttributeLoosensEmbeddingRequirement.md +19 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowConnectionAllowlistFrom.md +21 -0
- package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidAllowlistItemType.md +2 -2
- package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidHeader.md +2 -2
- package/build/src/third_party/issue-descriptions/connectionAllowlistInvalidUrlPattern.md +3 -3
- package/build/src/third_party/issue-descriptions/connectionAllowlistItemNotInnerList.md +2 -2
- package/build/src/third_party/issue-descriptions/connectionAllowlistMoreThanOneList.md +2 -2
- package/build/src/third_party/issue-descriptions/connectionAllowlistReportingEndpointNotToken.md +1 -1
- package/build/src/third_party/issue-descriptions/cookieCrossSiteRedirectDowngrade.md +3 -3
- package/build/src/third_party/issue-descriptions/cookieExcludeBlockedWithinRelatedWebsiteSet.md +1 -1
- package/build/src/third_party/issue-descriptions/cookieExcludePortMismatch.md +1 -1
- package/build/src/third_party/issue-descriptions/cookieExcludeSchemeMismatch.md +1 -1
- package/build/src/third_party/issue-descriptions/cookieExcludeThirdPartyPhaseoutSet.md +1 -1
- package/build/src/third_party/issue-descriptions/cookieWarnThirdPartyPhaseoutRead.md +1 -1
- package/build/src/third_party/issue-descriptions/corsAllowCredentialsRequired.md +1 -1
- package/build/src/third_party/issue-descriptions/corsDisabledScheme.md +2 -2
- package/build/src/third_party/issue-descriptions/corsDisallowedByMode.md +2 -2
- package/build/src/third_party/issue-descriptions/corsHeaderDisallowedByPreflightResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/corsInvalidHeaderValues.md +3 -3
- package/build/src/third_party/issue-descriptions/corsLocalNetworkAccessPermissionDenied.md +1 -1
- package/build/src/third_party/issue-descriptions/corsMethodDisallowedByPreflightResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/corsNoCorsRedirectModeNotFollow.md +1 -1
- package/build/src/third_party/issue-descriptions/corsOriginMismatch.md +3 -3
- package/build/src/third_party/issue-descriptions/corsPreflightResponseInvalid.md +2 -2
- package/build/src/third_party/issue-descriptions/corsRedirectContainsCredentials.md +3 -3
- package/build/src/third_party/issue-descriptions/corsWildcardOriginNotAllowed.md +4 -3
- package/build/src/third_party/issue-descriptions/cspEvalViolation.md +4 -4
- package/build/src/third_party/issue-descriptions/cspInlineViolation.md +2 -2
- package/build/src/third_party/issue-descriptions/cspTrustedTypesPolicyViolation.md +2 -2
- package/build/src/third_party/issue-descriptions/cspTrustedTypesSinkViolation.md +4 -4
- package/build/src/third_party/issue-descriptions/cspURLViolation.md +4 -4
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsEmptyList.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidContentType.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsFetchFailed.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsInvalidRecord.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidContentType.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestInvalidEmail.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestKeyBindingSigningFailed.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestRpOriginIsOpaque.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidContentType.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidSdJwt.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenMalformedSdJwt.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidAudience.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidIssuedAt.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidNonce.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidSdHash.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidTyp.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingAud.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingCnf.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingIat.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingNonce.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingSdHash.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbSignatureFailed.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmail.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmailVerified.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidHolderKey.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuedAt.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuer.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtJwksMissingKeys.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingCnf.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingEmail.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIat.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIss.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtSignatureFailed.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtUnsupportedHeaderAlg.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestUserLoggedOut.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownAccountsEndpointCrossOrigin.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidContentType.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownIssuanceEndpointCrossOrigin.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownListEmpty.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingAccountsEndpoint.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingIssuanceEndpoint.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownUnsupportedSigningAlgorithm.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsHttpNotFound.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsInvalidResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsNoResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestApprovalDeclined.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestCanceled.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorFetchingSignin.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestErrorIdToken.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenHttpNotFound.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidRequest.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenInvalidResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenNoResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestInvalidSigninResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestHttpNotFound.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestInvalidResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestManifestNoResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthRequestTooManyRequests.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidAccountsResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestInvalidConfigOrWellKnown.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoAccountSharingPermission.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoApiPermission.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNoReturningUserFromFetchedAccounts.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotIframe.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotPotentiallyTrustworthy.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSameOrigin.md +1 -1
- package/build/src/third_party/issue-descriptions/federatedAuthUserInfoRequestNotSignedInWithIdp.md +1 -1
- package/build/src/third_party/issue-descriptions/fetchingPartitionedBlobURL.md +2 -2
- package/build/src/third_party/issue-descriptions/genericBackUINavigationWouldSkipAd.md +4 -0
- package/build/src/third_party/issue-descriptions/genericFormAriaLabelledByToNonExistingIdError.md +2 -2
- package/build/src/third_party/issue-descriptions/genericFormAutocompleteAttributeEmptyError.md +1 -1
- package/build/src/third_party/issue-descriptions/genericFormDuplicateIdForInputError.md +1 -1
- package/build/src/third_party/issue-descriptions/genericFormEmptyIdAndNameAttributesForInputError.md +2 -2
- package/build/src/third_party/issue-descriptions/genericFormInputAssignedAutocompleteValueToIdOrNameAttributeError.md +2 -2
- package/build/src/third_party/issue-descriptions/genericFormInputHasWrongButWellIntendedAutocompleteValueError.md +1 -1
- package/build/src/third_party/issue-descriptions/genericFormInputWithNoLabelError.md +1 -1
- package/build/src/third_party/issue-descriptions/genericFormLabelForMatchesNonExistingIdError.md +2 -2
- package/build/src/third_party/issue-descriptions/genericFormLabelForNameError.md +1 -1
- package/build/src/third_party/issue-descriptions/genericFormLabelHasNeitherForNorNestedInputError.md +1 -1
- package/build/src/third_party/issue-descriptions/genericFormModelContextMissingToolName.md +1 -1
- package/build/src/third_party/issue-descriptions/genericFormModelContextParameterMissingName.md +1 -1
- package/build/src/third_party/issue-descriptions/genericFormModelContextRequiredParameterMissingName.md +1 -1
- package/build/src/third_party/issue-descriptions/genericNavigationEntryMarkedSkippable.md +2 -2
- package/build/src/third_party/issue-descriptions/heavyAd.md +1 -1
- package/build/src/third_party/issue-descriptions/lazyLoadImageZeroSize.md +7 -0
- package/build/src/third_party/issue-descriptions/navigatingPartitionedBlobURL.md +3 -3
- package/build/src/third_party/issue-descriptions/permissionElementActivationDisabled.md +1 -1
- package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluder.md +1 -1
- package/build/src/third_party/issue-descriptions/permissionElementActivationDisabledWithOccluderParent.md +1 -1
- package/build/src/third_party/issue-descriptions/permissionElementFencedFrameDisallowed.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooLarge.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementFontSizeTooSmall.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementInsetBoxShadowUnsupported.md +1 -1
- package/build/src/third_party/issue-descriptions/permissionElementInvalidDisplayStyle.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementInvalidSizeValue.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementInvalidType.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementInvalidTypeActivation.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementNonOpaqueColor.md +1 -1
- package/build/src/third_party/issue-descriptions/permissionElementPaddingBottomUnsupported.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementPaddingRightUnsupported.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementRequestInProgress.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementSecurityChecksFailed.md +1 -1
- package/build/src/third_party/issue-descriptions/permissionElementTypeNotSupported.md +2 -2
- package/build/src/third_party/issue-descriptions/permissionElementUntrustedEvent.md +1 -1
- package/build/src/third_party/issue-descriptions/placeholderDescriptionForInvisibleIssues.md +1 -1
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedOptGroupChild.md +1 -1
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityDisallowedSelectChild.md +1 -1
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentAttributesSelectDescendant.md +1 -1
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentLegendChild.md +1 -1
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityInteractiveContentOptionChild.md +1 -1
- package/build/src/third_party/issue-descriptions/selectElementAccessibilityNonPhrasingContentOptionChild.md +1 -1
- package/build/src/third_party/issue-descriptions/selectivePermissionsIntervention.md +3 -3
- package/build/src/third_party/issue-descriptions/sharedArrayBuffer.md +1 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorCrossOriginNoCorsRequest.md +1 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorMatchingDictionaryNotUsed.md +1 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorUnexpectedContentDictionaryHeader.md +1 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorCossOriginNoCorsRequest.md +1 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorDisallowedBySettings.md +1 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorExpiredResponse.md +1 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorFeatureDisabled.md +2 -2
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorInvalidStructuredHeader.md +1 -1
- package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNavigationRequest.md +2 -2
- package/build/src/third_party/issue-descriptions/sriInvalidSignatureHeader.md +4 -4
- package/build/src/third_party/issue-descriptions/sriInvalidSignatureInputHeader.md +4 -4
- package/build/src/third_party/issue-descriptions/sriMissingSignatureHeader.md +1 -1
- package/build/src/third_party/issue-descriptions/sriMissingSignatureInputHeader.md +2 -2
- package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsIncorrectLength.md +2 -2
- package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsNotByteSequence.md +4 -4
- package/build/src/third_party/issue-descriptions/sriSignatureHeaderValueIsParameterized.md +4 -4
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentName.md +1 -1
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidComponentType.md +2 -2
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidDerivedComponentParameter.md +1 -1
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidHeaderComponentParameter.md +1 -1
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderInvalidParameter.md +2 -2
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderKeyIdLength.md +5 -5
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingLabel.md +3 -3
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderMissingRequiredParameters.md +2 -2
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueMissingComponents.md +2 -2
- package/build/src/third_party/issue-descriptions/sriSignatureInputHeaderValueNotInnerList.md +2 -2
- package/build/src/third_party/issue-descriptions/sriValidationFailedIntegrityMismatch.md +4 -4
- package/build/src/third_party/issue-descriptions/sriValidationFailedInvalidLength.md +3 -3
- package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureExpired.md +2 -2
- package/build/src/third_party/issue-descriptions/sriValidationFailedSignatureMismatch.md +2 -2
- package/build/src/third_party/issue-descriptions/stylesheetLateImport.md +1 -1
- package/build/src/third_party/issue-descriptions/summaryElementAccessibilityInteractiveContentSummaryDescendant.md +1 -1
- package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestLength.md +2 -2
- package/build/src/third_party/issue-descriptions/unencodedDigestIncorrectDigestType.md +3 -3
- package/build/src/third_party/issue-descriptions/unencodedDigestMalformedDictionary.md +2 -2
- package/build/src/third_party/issue-descriptions/unencodedDigestUnknownAlgorithm.md +1 -1
- package/build/src/third_party/lighthouse-devtools-mcp-bundle.js +3177 -6340
- package/build/src/tools/ToolDefinition.js +2 -2
- package/build/src/tools/categories.js +3 -0
- package/build/src/tools/console.js +13 -0
- package/build/src/tools/emulation.js +30 -3
- package/build/src/tools/extensions.js +8 -1
- package/build/src/tools/input.js +246 -128
- package/build/src/tools/lighthouse.js +14 -7
- package/build/src/tools/memory.js +289 -25
- package/build/src/tools/network.js +10 -7
- package/build/src/tools/pages.js +33 -92
- package/build/src/tools/performance.js +67 -52
- package/build/src/tools/pwa.js +128 -0
- package/build/src/tools/screencast.js +35 -12
- package/build/src/tools/screenshot.js +234 -75
- package/build/src/tools/script.js +130 -45
- package/build/src/tools/slim/tools.js +3 -0
- package/build/src/tools/snapshot.js +7 -4
- package/build/src/tools/thirdPartyDeveloper.js +19 -9
- package/build/src/tools/tools.js +3 -1
- package/build/src/tools/webmcp.js +3 -1
- package/build/src/utils/WaitForHelper.js +299 -0
- package/build/src/utils/check-for-updates.js +8 -2
- package/build/src/utils/files.js +42 -3
- package/build/src/utils/logger.js +51 -0
- package/build/src/utils/polyfill.js +11 -0
- package/build/src/utils/url.js +43 -0
- package/build/src/version.js +1 -1
- package/package.json +42 -33
- package/build/src/DevToolsConnectionAdapter.js +0 -70
- package/build/src/HeapSnapshotManager.js +0 -110
- package/build/src/Mutex.js +0 -38
- package/build/src/WaitForHelper.js +0 -195
- package/build/src/bin/cliDefinitions.js +0 -616
- package/build/src/logger.js +0 -37
- package/build/src/polyfill.js +0 -8
- package/build/src/telemetry/metricUtils.js +0 -15
- package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeRead.md +0 -8
- package/build/src/third_party/issue-descriptions/SameSiteExcludeContextDowngradeSet.md +0 -8
- package/build/src/third_party/issue-descriptions/SameSiteExcludeNavigationContextDowngrade.md +0 -8
- package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeRead.md +0 -8
- package/build/src/third_party/issue-descriptions/SameSiteWarnCrossDowngradeSet.md +0 -8
- package/build/src/third_party/issue-descriptions/SameSiteWarnStrictLaxDowngradeStrict.md +0 -8
- package/build/src/third_party/issue-descriptions/arInsecureContext.md +0 -7
- package/build/src/third_party/issue-descriptions/arInvalidInfoHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arInvalidRegisterOsSourceHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arInvalidRegisterOsTriggerHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arInvalidRegisterSourceHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arInvalidRegisterTriggerHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arNavigationRegistrationUniqueScopeAlreadySet.md +0 -5
- package/build/src/third_party/issue-descriptions/arNavigationRegistrationWithoutTransientUserActivation.md +0 -6
- package/build/src/third_party/issue-descriptions/arNoRegisterOsSourceHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arNoRegisterOsTriggerHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arNoRegisterSourceHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arNoRegisterTriggerHeader.md +0 -5
- package/build/src/third_party/issue-descriptions/arNoWebOrOsSupport.md +0 -4
- package/build/src/third_party/issue-descriptions/arOsSourceIgnored.md +0 -18
- package/build/src/third_party/issue-descriptions/arOsTriggerIgnored.md +0 -19
- package/build/src/third_party/issue-descriptions/arPermissionPolicyDisabled.md +0 -8
- package/build/src/third_party/issue-descriptions/arSourceAndTriggerHeaders.md +0 -9
- package/build/src/third_party/issue-descriptions/arSourceIgnored.md +0 -13
- package/build/src/third_party/issue-descriptions/arTriggerIgnored.md +0 -12
- package/build/src/third_party/issue-descriptions/arUntrustworthyReportingOrigin.md +0 -10
- package/build/src/third_party/issue-descriptions/arWebAndOsHeaders.md +0 -11
- package/build/src/utils/string.js +0 -37
- package/build/src/utils/types.js +0 -7
package/build/src/index.js
CHANGED
|
@@ -5,22 +5,43 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Modified by Opera Software AS.
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
9
|
-
import { loadIssueDescriptions } from './
|
|
10
|
-
import { logger } from './logger.js';
|
|
8
|
+
import { ensureBrowserConnected, ensureBrowserLaunched } from './browser.js';
|
|
9
|
+
import { loadIssueDescriptions } from './devtools/issueDescriptions.js';
|
|
11
10
|
import { McpContext } from './McpContext.js';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
11
|
+
import { BROWSER_EXPOSURE_DISCLAIMER, showUsageStatisticsDisclaimer, } from './opera/policy.js';
|
|
12
|
+
import { createOperaToolHooks } from './opera/toolHandlerHooks.js';
|
|
13
|
+
import { buildLaunchOptions } from './opera/browserLaunch.js';
|
|
14
|
+
import { ClearcutLogger } from './telemetry/ClearcutLogger.js';
|
|
15
|
+
import { FilePersistence } from './telemetry/persistence.js';
|
|
16
16
|
import { McpServer, SetLevelRequestSchema, ListRootsResultSchema, RootsListChangedNotificationSchema, } from './third_party/index.js';
|
|
17
|
-
import {
|
|
18
|
-
import { pageIdSchema } from './tools/ToolDefinition.js';
|
|
17
|
+
import { ToolHandler } from './ToolHandler.js';
|
|
19
18
|
import { createTools } from './tools/tools.js';
|
|
19
|
+
import { logger } from './utils/logger.js';
|
|
20
|
+
import { Mutex } from './third_party/index.js';
|
|
20
21
|
import { VERSION } from './version.js';
|
|
21
22
|
export { buildFlag } from './ToolHandler.js';
|
|
23
|
+
/**
|
|
24
|
+
* Timeout for a `roots/list` that a tool call is waiting on, matching the 5s
|
|
25
|
+
* default used for page operations. `getContext()` awaits it while
|
|
26
|
+
* `ToolHandler` holds the tool mutex, so leaving it unbounded lets a client
|
|
27
|
+
* that negotiates `roots` but does not answer block every tool for the SDK's
|
|
28
|
+
* default of 60s. Background refreshes are not bounded by this, so roots a
|
|
29
|
+
* slow client sends late still land.
|
|
30
|
+
*/
|
|
31
|
+
const ROOTS_REQUEST_TIMEOUT = 5_000;
|
|
22
32
|
export async function createMcpServer(serverArgs, options) {
|
|
23
|
-
|
|
33
|
+
// Opera forces `usageStatistics` off in ./opera/policy.ts, so this stays
|
|
34
|
+
// dormant. Kept identical to upstream so the seam is the policy, not this file.
|
|
35
|
+
if (serverArgs.usageStatistics) {
|
|
36
|
+
ClearcutLogger.initialize({
|
|
37
|
+
persistence: new FilePersistence(),
|
|
38
|
+
logFile: serverArgs.logFile,
|
|
39
|
+
appVersion: VERSION,
|
|
40
|
+
clearcutEndpoint: serverArgs.clearcutEndpoint,
|
|
41
|
+
clearcutForceFlushIntervalMs: serverArgs.clearcutForceFlushIntervalMs,
|
|
42
|
+
clearcutIncludePidHeader: serverArgs.clearcutIncludePidHeader,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
24
45
|
const server = new McpServer({
|
|
25
46
|
name: 'chrome_devtools',
|
|
26
47
|
title: 'Chrome DevTools MCP server',
|
|
@@ -29,22 +50,30 @@ export async function createMcpServer(serverArgs, options) {
|
|
|
29
50
|
server.server.setRequestHandler(SetLevelRequestSchema, () => {
|
|
30
51
|
return {};
|
|
31
52
|
});
|
|
32
|
-
|
|
53
|
+
// Roots are client state rather than browser state, so the last listing stays
|
|
54
|
+
// valid across browser reconnects and only the client can invalidate it, via
|
|
55
|
+
// the `roots/list_changed` notification handled below
|
|
56
|
+
let lastRoots;
|
|
57
|
+
// `timeout` is only passed where a tool call is waiting on the result – the
|
|
58
|
+
// background refreshes below block nobody, so bounding them would just discard
|
|
59
|
+
// roots a slow client was about to send
|
|
60
|
+
const updateRoots = async (timeout) => {
|
|
33
61
|
if (!server.server.getClientCapabilities()?.roots) {
|
|
34
62
|
return;
|
|
35
63
|
}
|
|
36
64
|
try {
|
|
37
|
-
const roots = await server.server.request({ method: 'roots/list' }, ListRootsResultSchema);
|
|
38
|
-
|
|
65
|
+
const roots = await server.server.request({ method: 'roots/list' }, ListRootsResultSchema, timeout === undefined ? undefined : { timeout });
|
|
66
|
+
lastRoots = roots.roots;
|
|
67
|
+
context?.setRoots(lastRoots);
|
|
39
68
|
}
|
|
40
69
|
catch (e) {
|
|
41
|
-
logger('Failed to list roots', e);
|
|
70
|
+
logger?.('Failed to list roots', e);
|
|
42
71
|
}
|
|
43
72
|
};
|
|
44
73
|
server.server.oninitialized = () => {
|
|
45
74
|
const clientName = server.server.getClientVersion()?.name;
|
|
46
75
|
if (clientName) {
|
|
47
|
-
|
|
76
|
+
ClearcutLogger.get()?.setClientName(clientName);
|
|
48
77
|
}
|
|
49
78
|
if (server.server.getClientCapabilities()?.roots) {
|
|
50
79
|
void updateRoots();
|
|
@@ -52,16 +81,22 @@ export async function createMcpServer(serverArgs, options) {
|
|
|
52
81
|
void updateRoots();
|
|
53
82
|
});
|
|
54
83
|
}
|
|
84
|
+
else if (!serverArgs.allowUnrestrictedPaths) {
|
|
85
|
+
console.warn('[chrome-devtools-mcp] The connecting client did not negotiate the MCP roots ' +
|
|
86
|
+
'capability. File-writing tools will be restricted to the OS temp directory. ' +
|
|
87
|
+
'To restore the previous unrestricted behavior, start the server with ' +
|
|
88
|
+
'--allow-unrestricted-paths.');
|
|
89
|
+
}
|
|
55
90
|
};
|
|
56
91
|
let context;
|
|
57
|
-
let browserHasOperaFlags = false;
|
|
58
92
|
async function getContext() {
|
|
59
|
-
const chromeArgs = (serverArgs.chromeArg ?? []).map(String);
|
|
60
|
-
const ignoreDefaultChromeArgs = (serverArgs.ignoreDefaultChromeArg ?? []).map(String);
|
|
61
|
-
if (serverArgs.proxyServer) {
|
|
62
|
-
chromeArgs.push(`--proxy-server=${serverArgs.proxyServer}`);
|
|
63
|
-
}
|
|
64
93
|
const devtools = serverArgs.experimentalDevtools ?? false;
|
|
94
|
+
const blocklist = serverArgs.blockedUrlPattern
|
|
95
|
+
? serverArgs.blockedUrlPattern.map(String)
|
|
96
|
+
: undefined;
|
|
97
|
+
const allowlist = serverArgs.allowedUrlPattern
|
|
98
|
+
? serverArgs.allowedUrlPattern.map(String)
|
|
99
|
+
: undefined;
|
|
65
100
|
const browser = serverArgs.browserUrl || serverArgs.wsEndpoint || serverArgs.autoConnect
|
|
66
101
|
? await ensureBrowserConnected({
|
|
67
102
|
browserURL: serverArgs.browserUrl,
|
|
@@ -73,232 +108,63 @@ export async function createMcpServer(serverArgs, options) {
|
|
|
73
108
|
: undefined,
|
|
74
109
|
userDataDir: serverArgs.userDataDir,
|
|
75
110
|
devtools,
|
|
111
|
+
blocklist,
|
|
112
|
+
allowlist,
|
|
76
113
|
})
|
|
77
|
-
: await ensureBrowserLaunched(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
isolated: serverArgs.isolated ?? false,
|
|
82
|
-
userDataDir: serverArgs.userDataDir,
|
|
83
|
-
logFile: options.logFile,
|
|
84
|
-
viewport: serverArgs.viewport,
|
|
85
|
-
chromeArgs,
|
|
86
|
-
ignoreDefaultChromeArgs,
|
|
87
|
-
acceptInsecureCerts: serverArgs.acceptInsecureCerts,
|
|
114
|
+
: await ensureBrowserLaunched(
|
|
115
|
+
// Pass the already-derived values so the launched browser uses the
|
|
116
|
+
// exact same flags as the connected branch and `McpContext`.
|
|
117
|
+
buildLaunchOptions(serverArgs, options.logFile, {
|
|
88
118
|
devtools,
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
});
|
|
119
|
+
blocklist,
|
|
120
|
+
allowlist,
|
|
121
|
+
}));
|
|
92
122
|
if (context?.browser !== browser) {
|
|
123
|
+
context?.dispose();
|
|
93
124
|
context = await McpContext.from(browser, logger, {
|
|
94
125
|
experimentalDevToolsDebugging: devtools,
|
|
95
126
|
experimentalIncludeAllPages: serverArgs.experimentalIncludeAllPages,
|
|
96
127
|
performanceCrux: serverArgs.performanceCrux,
|
|
128
|
+
allowList: allowlist,
|
|
129
|
+
blocklist: blocklist,
|
|
130
|
+
allowUnrestrictedPaths: serverArgs.allowUnrestrictedPaths,
|
|
131
|
+
// Surfaces a one-time note in the next response after a reconnect.
|
|
132
|
+
reconnected: context !== undefined,
|
|
97
133
|
});
|
|
98
|
-
|
|
134
|
+
if (lastRoots === undefined) {
|
|
135
|
+
// Nothing listed yet, so this call has to wait – bounded, since it is
|
|
136
|
+
// holding the tool mutex, and a later background refresh still lands
|
|
137
|
+
await updateRoots(ROOTS_REQUEST_TIMEOUT);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
// Carry the known roots over and refresh out of band, so a reconnect
|
|
141
|
+
// never pays for a client round-trip
|
|
142
|
+
context.setRoots(lastRoots);
|
|
143
|
+
void updateRoots();
|
|
144
|
+
}
|
|
99
145
|
}
|
|
100
146
|
return context;
|
|
101
147
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
context?.dispose();
|
|
111
|
-
context = undefined;
|
|
112
|
-
browserHasOperaFlags = false;
|
|
113
|
-
await closeBrowserIfOpen();
|
|
114
|
-
const chromeArgs = [
|
|
115
|
-
'--disable-blink-features=AutomationControlled',
|
|
116
|
-
...(serverArgs.chromeArg ?? []).map(String),
|
|
117
|
-
];
|
|
118
|
-
if (serverArgs.proxyServer) {
|
|
119
|
-
chromeArgs.push(`--proxy-server=${serverArgs.proxyServer}`);
|
|
120
|
-
}
|
|
121
|
-
await ensureBrowserLaunched({
|
|
122
|
-
headless: serverArgs.headless,
|
|
123
|
-
executablePath: serverArgs.executablePath,
|
|
124
|
-
channel: serverArgs.channel,
|
|
125
|
-
isolated: serverArgs.isolated ?? false,
|
|
126
|
-
userDataDir: serverArgs.userDataDir,
|
|
127
|
-
logFile: options.logFile,
|
|
128
|
-
viewport: serverArgs.viewport,
|
|
129
|
-
chromeArgs,
|
|
130
|
-
ignoreDefaultChromeArgs: (serverArgs.ignoreDefaultChromeArg ?? []).map(String),
|
|
131
|
-
acceptInsecureCerts: serverArgs.acceptInsecureCerts,
|
|
132
|
-
devtools: serverArgs.experimentalDevtools ?? false,
|
|
133
|
-
enableExtensions: serverArgs.categoryExtensions,
|
|
134
|
-
viaCli: serverArgs.viaCli,
|
|
135
|
-
});
|
|
136
|
-
browserHasOperaFlags = true;
|
|
137
|
-
}
|
|
148
|
+
const operaHooks = createOperaToolHooks({
|
|
149
|
+
serverArgs,
|
|
150
|
+
logFile: options.logFile,
|
|
151
|
+
resetContext: () => {
|
|
152
|
+
context?.dispose();
|
|
153
|
+
context = undefined;
|
|
154
|
+
},
|
|
155
|
+
});
|
|
138
156
|
const toolMutex = new Mutex();
|
|
139
157
|
function registerTool(tool) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
if (tool.annotations.category === ToolCategory.PERFORMANCE &&
|
|
145
|
-
serverArgs.categoryPerformance === false) {
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
if (tool.annotations.category === ToolCategory.NETWORK &&
|
|
149
|
-
serverArgs.categoryNetwork === false) {
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
if (tool.annotations.category === ToolCategory.EXTENSIONS &&
|
|
153
|
-
!serverArgs.categoryExtensions) {
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
if (tool.annotations.category === ToolCategory.WEBMCP &&
|
|
157
|
-
!serverArgs.categoryExperimentalWebmcp) {
|
|
158
|
+
const toolHandler = new ToolHandler(tool, serverArgs, getContext, toolMutex, operaHooks);
|
|
159
|
+
if (!toolHandler.shouldRegister) {
|
|
158
160
|
return;
|
|
159
161
|
}
|
|
160
|
-
if (tool.annotations.category === ToolCategory.THIRD_PARTY &&
|
|
161
|
-
!serverArgs.categoryExperimentalThirdParty) {
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
if (tool.annotations.conditions?.includes('experimentalVision') &&
|
|
165
|
-
!serverArgs.experimentalVision) {
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
if (tool.annotations.conditions?.includes('experimentalInteropTools') &&
|
|
169
|
-
!serverArgs.experimentalInteropTools) {
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
if (tool.annotations.conditions?.includes('experimentalScreencast') &&
|
|
173
|
-
!serverArgs.experimentalScreencast) {
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
if (tool.annotations.conditions?.includes('experimentalMemory') &&
|
|
177
|
-
!serverArgs.experimentalMemory) {
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
const schema = 'pageScoped' in tool &&
|
|
181
|
-
tool.pageScoped &&
|
|
182
|
-
serverArgs.experimentalPageIdRouting &&
|
|
183
|
-
!serverArgs.slim
|
|
184
|
-
? { ...tool.schema, ...pageIdSchema }
|
|
185
|
-
: tool.schema;
|
|
186
162
|
server.registerTool(tool.name, {
|
|
187
163
|
description: tool.description,
|
|
188
|
-
inputSchema:
|
|
164
|
+
inputSchema: toolHandler.registeredInputSchema,
|
|
189
165
|
annotations: tool.annotations,
|
|
190
166
|
}, async (params, extra) => {
|
|
191
|
-
|
|
192
|
-
? null
|
|
193
|
-
: await toolMutex.acquire();
|
|
194
|
-
const startTime = Date.now();
|
|
195
|
-
let success = false;
|
|
196
|
-
try {
|
|
197
|
-
const isLaunchMode = !serverArgs.browserUrl &&
|
|
198
|
-
!serverArgs.wsEndpoint &&
|
|
199
|
-
!serverArgs.autoConnect;
|
|
200
|
-
const needsOperaFlags = tool.name === 'opera_do' || tool.name === 'opera_research';
|
|
201
|
-
const browserConnected = getCurrentBrowser()?.connected ?? false;
|
|
202
|
-
if (isLaunchMode) {
|
|
203
|
-
if (needsOperaFlags &&
|
|
204
|
-
!(browserHasOperaFlags && browserConnected)) {
|
|
205
|
-
await restartBrowserForOpera();
|
|
206
|
-
}
|
|
207
|
-
else if (!needsOperaFlags &&
|
|
208
|
-
browserHasOperaFlags &&
|
|
209
|
-
browserConnected) {
|
|
210
|
-
await restartBrowserWithoutOperaFlags();
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
logger(`${tool.name} request: ${JSON.stringify(params, null, ' ')}`);
|
|
214
|
-
const context = await getContext();
|
|
215
|
-
logger(`${tool.name} context: resolved`);
|
|
216
|
-
await context.detectOpenDevToolsWindows();
|
|
217
|
-
const logCallback = (message) => {
|
|
218
|
-
// `logger` carries the MCP request ID so the opera-cli bridge can route
|
|
219
|
-
// this chunk to the correct HTTP response (see bridge.ts requestLoggers).
|
|
220
|
-
// `data` stays a plain string so non-bridge MCP hosts (Claude Desktop,
|
|
221
|
-
// VS Code, etc.) continue to render it as readable text.
|
|
222
|
-
void extra.sendNotification({
|
|
223
|
-
method: 'notifications/message',
|
|
224
|
-
params: {
|
|
225
|
-
level: 'info',
|
|
226
|
-
data: message,
|
|
227
|
-
logger: String(extra.requestId),
|
|
228
|
-
},
|
|
229
|
-
});
|
|
230
|
-
};
|
|
231
|
-
const response = serverArgs.slim
|
|
232
|
-
? new SlimMcpResponse(serverArgs, logCallback)
|
|
233
|
-
: new McpResponse(serverArgs, logCallback);
|
|
234
|
-
try {
|
|
235
|
-
if ('pageScoped' in tool && tool.pageScoped) {
|
|
236
|
-
const page = serverArgs.experimentalPageIdRouting &&
|
|
237
|
-
params.pageId &&
|
|
238
|
-
!serverArgs.slim
|
|
239
|
-
? context.getPageById(params.pageId)
|
|
240
|
-
: context.getSelectedMcpPage();
|
|
241
|
-
response.setPage(page);
|
|
242
|
-
if (tool.blockedByDialog) {
|
|
243
|
-
page.throwIfDialogOpen();
|
|
244
|
-
}
|
|
245
|
-
await tool.handler({
|
|
246
|
-
params,
|
|
247
|
-
page,
|
|
248
|
-
signal: extra.signal,
|
|
249
|
-
}, response, context);
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
await tool.handler(
|
|
253
|
-
// @ts-expect-error types do not match.
|
|
254
|
-
{
|
|
255
|
-
params,
|
|
256
|
-
signal: extra.signal,
|
|
257
|
-
}, response, context);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
catch (err) {
|
|
261
|
-
response.setError(err);
|
|
262
|
-
}
|
|
263
|
-
const { content, structuredContent } = await response.handle(tool.name, context);
|
|
264
|
-
const result = {
|
|
265
|
-
content,
|
|
266
|
-
};
|
|
267
|
-
if (response.error) {
|
|
268
|
-
result.isError = true;
|
|
269
|
-
}
|
|
270
|
-
success = true;
|
|
271
|
-
if (serverArgs.experimentalStructuredContent) {
|
|
272
|
-
result.structuredContent = structuredContent;
|
|
273
|
-
}
|
|
274
|
-
return result;
|
|
275
|
-
}
|
|
276
|
-
catch (err) {
|
|
277
|
-
logger(`${tool.name} error:`, err, err?.stack);
|
|
278
|
-
let errorText = err && 'message' in err ? err.message : String(err);
|
|
279
|
-
if ('cause' in err && err.cause) {
|
|
280
|
-
errorText += `\nCause: ${err.cause.message}`;
|
|
281
|
-
}
|
|
282
|
-
return {
|
|
283
|
-
content: [
|
|
284
|
-
{
|
|
285
|
-
type: 'text',
|
|
286
|
-
text: errorText,
|
|
287
|
-
},
|
|
288
|
-
],
|
|
289
|
-
isError: true,
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
finally {
|
|
293
|
-
void clearcutLogger?.logToolInvocation({
|
|
294
|
-
toolName: tool.name,
|
|
295
|
-
params: params,
|
|
296
|
-
schema: tool.schema,
|
|
297
|
-
success,
|
|
298
|
-
latencyMs: bucketizeLatency(Date.now() - startTime),
|
|
299
|
-
});
|
|
300
|
-
guard?.dispose();
|
|
301
|
-
}
|
|
167
|
+
return await toolHandler.handle(params, extra);
|
|
302
168
|
});
|
|
303
169
|
}
|
|
304
170
|
const tools = createTools(serverArgs);
|
|
@@ -306,14 +172,17 @@ export async function createMcpServer(serverArgs, options) {
|
|
|
306
172
|
registerTool(tool);
|
|
307
173
|
}
|
|
308
174
|
await loadIssueDescriptions();
|
|
309
|
-
return { server
|
|
175
|
+
return { server };
|
|
310
176
|
}
|
|
311
177
|
export const logDisclaimers = (args) => {
|
|
312
|
-
console.error(
|
|
313
|
-
debug, and modify any data in the browser or DevTools.
|
|
314
|
-
Avoid sharing sensitive or personal information that you do not want to share with MCP clients.`);
|
|
178
|
+
console.error(BROWSER_EXPOSURE_DISCLAIMER);
|
|
315
179
|
if (!args.slim && args.performanceCrux) {
|
|
316
180
|
console.error(`Performance tools may send trace URLs to the Google CrUX API to fetch real-user experience data. To disable, run with --no-performance-crux.`);
|
|
317
181
|
}
|
|
182
|
+
if (showUsageStatisticsDisclaimer() && !args.slim && args.usageStatistics) {
|
|
183
|
+
console.error(`
|
|
184
|
+
Google collects usage statistics to improve Chrome DevTools MCP. To opt-out, run with --no-usage-statistics.
|
|
185
|
+
For more details, visit: https://github.com/ChromeDevTools/chrome-devtools-mcp#usage-statistics`);
|
|
186
|
+
}
|
|
318
187
|
};
|
|
319
188
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Opera Software AS.
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Single source of truth for every product string that differs from upstream
|
|
8
|
+
* `chrome-devtools-mcp`.
|
|
9
|
+
*
|
|
10
|
+
* Upstream files reference these constants instead of hardcoding literals, so
|
|
11
|
+
* the merge seam for rebranding is one changed line per string rather than a
|
|
12
|
+
* forked copy of the file.
|
|
13
|
+
*/
|
|
14
|
+
/** npm package name. Doubles as the MCP server binary name. */
|
|
15
|
+
export const PACKAGE_NAME = 'opera-devtools-mcp';
|
|
16
|
+
/** MCP server binary / daemon app name, also used as `process.title`. */
|
|
17
|
+
export const MCP_BIN_NAME = PACKAGE_NAME;
|
|
18
|
+
/** CLI binary name, e.g. `opera-devtools start`. */
|
|
19
|
+
export const CLI_BIN_NAME = 'opera-devtools';
|
|
20
|
+
/** Human-readable product name used in log lines. */
|
|
21
|
+
export const PRODUCT_NAME = 'Opera DevTools MCP Server';
|
|
22
|
+
/** Public repository, referenced from help text and disclaimers. */
|
|
23
|
+
export const REPO_URL = 'https://github.com/operasoftware/opera-devtools-mcp';
|
|
24
|
+
/** Directory under `$HOME/.cache` holding the update-check snapshot. */
|
|
25
|
+
export const CACHE_DIR_NAME = PACKAGE_NAME;
|
|
26
|
+
/** Bin script the daemon spawns to run the MCP server. */
|
|
27
|
+
export const INDEX_SCRIPT_NAME = `${MCP_BIN_NAME}.js`;
|
|
28
|
+
/** Upstream reads `CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS`. */
|
|
29
|
+
export const ENV_NO_USAGE_STATISTICS = 'OPERA_DEVTOOLS_NO_USAGE_STATISTICS';
|
|
30
|
+
/** Upstream reads `CHROME_DEVTOOLS_MCP_NO_UPDATE_CHECKS`. */
|
|
31
|
+
export const ENV_NO_UPDATE_CHECKS = 'OPERA_DEVTOOLS_NO_UPDATE_CHECKS';
|
|
32
|
+
/** Upstream reads `CHROME_DEVTOOLS_MCP_CRASH_ON_UNCAUGHT`. */
|
|
33
|
+
export const ENV_CRASH_ON_UNCAUGHT = 'OPERA_DEVTOOLS_CRASH_ON_UNCAUGHT';
|
|
34
|
+
//# sourceMappingURL=branding.js.map
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Opera Norway AS. All rights reserved.
|
|
4
|
+
*
|
|
5
|
+
* This file is an original work developed by Opera.
|
|
6
|
+
*/
|
|
7
|
+
import { closeBrowserIfOpen, ensureBrowserLaunched, getCurrentBrowser, } from '../browser.js';
|
|
8
|
+
import { buildLaunchOptions } from './browserLaunch.js';
|
|
9
|
+
import { logger } from '../utils/logger.js';
|
|
10
|
+
/**
|
|
11
|
+
* Opera's AI features refuse to run when the page reports itself as
|
|
12
|
+
* automation-controlled, so the browser has to be launched with this flag for
|
|
13
|
+
* them. It is deliberately NOT applied to every launch: it changes observable
|
|
14
|
+
* page behaviour, which would silently alter results for ordinary DevTools
|
|
15
|
+
* tools.
|
|
16
|
+
*/
|
|
17
|
+
export const OPERA_AUTOMATION_FLAGS = [
|
|
18
|
+
'--disable-blink-features=AutomationControlled',
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Only the tools that drive Opera's agentic AI need the automation flags.
|
|
22
|
+
* `opera_chat`, `opera_make` and `opera_list_models` talk to the service worker
|
|
23
|
+
* directly and work without them.
|
|
24
|
+
*/
|
|
25
|
+
const TOOLS_REQUIRING_OPERA_FLAGS = new Set(['opera_do', 'opera_research']);
|
|
26
|
+
export function toolRequiresOperaFlags(toolName) {
|
|
27
|
+
return TOOLS_REQUIRING_OPERA_FLAGS.has(toolName);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Tracks whether the currently running browser was launched with
|
|
31
|
+
* {@link OPERA_AUTOMATION_FLAGS}. Module-level rather than per-server because
|
|
32
|
+
* the browser itself is a module-level singleton in `../browser.ts`.
|
|
33
|
+
*/
|
|
34
|
+
let browserHasOperaFlags = false;
|
|
35
|
+
export function browserWasLaunchedWithOperaFlags() {
|
|
36
|
+
return browserHasOperaFlags;
|
|
37
|
+
}
|
|
38
|
+
/** Test seam: forget what we believe about the current browser. */
|
|
39
|
+
export function resetOperaFlagState() {
|
|
40
|
+
browserHasOperaFlags = false;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* True when this server launched the browser itself. When the user attached to
|
|
44
|
+
* an existing browser we must never kill and relaunch it.
|
|
45
|
+
*/
|
|
46
|
+
function isLaunchMode(serverArgs) {
|
|
47
|
+
return (!serverArgs.browserUrl && !serverArgs.wsEndpoint && !serverArgs.autoConnect);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Makes sure the running browser has (or lacks) the Opera automation flags to
|
|
51
|
+
* match what `toolName` needs, relaunching it if not. No-op when we did not
|
|
52
|
+
* launch the browser ourselves, or when the flags already match.
|
|
53
|
+
*/
|
|
54
|
+
export async function ensureBrowserFlagsForTool(toolName, serverArgs, logFile, control,
|
|
55
|
+
// Injected so tests do not need a real browser singleton.
|
|
56
|
+
deps = { closeBrowserIfOpen, ensureBrowserLaunched }) {
|
|
57
|
+
if (!isLaunchMode(serverArgs)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const needsOperaFlags = toolRequiresOperaFlags(toolName);
|
|
61
|
+
const browserConnected = getCurrentBrowser()?.connected ?? false;
|
|
62
|
+
// A disconnected browser tells us nothing about the flags of the next one.
|
|
63
|
+
if (needsOperaFlags && browserHasOperaFlags && browserConnected) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (!needsOperaFlags && !(browserHasOperaFlags && browserConnected)) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
logger?.(`Relaunching browser ${needsOperaFlags ? 'with' : 'without'} Opera automation flags for ${toolName}`);
|
|
70
|
+
control.resetContext();
|
|
71
|
+
browserHasOperaFlags = false;
|
|
72
|
+
await deps.closeBrowserIfOpen();
|
|
73
|
+
if (needsOperaFlags) {
|
|
74
|
+
// Launch options come from the shared `buildLaunchOptions` so the relaunch
|
|
75
|
+
// applies exactly the same flags (including blocklist/allowlist and proxy)
|
|
76
|
+
// as the normal launch in `index.ts`, plus the automation flags.
|
|
77
|
+
await deps.ensureBrowserLaunched(buildLaunchOptions(serverArgs, logFile, {
|
|
78
|
+
extraChromeArgs: OPERA_AUTOMATION_FLAGS,
|
|
79
|
+
}));
|
|
80
|
+
browserHasOperaFlags = true;
|
|
81
|
+
}
|
|
82
|
+
// Otherwise leave the browser closed: getContext() relaunches it without the
|
|
83
|
+
// Opera flags on the next call.
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=browserFlags.js.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Opera Norway AS. All rights reserved.
|
|
4
|
+
*
|
|
5
|
+
* This file is an original work developed by Opera.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Single source of truth for mapping parsed server args to
|
|
9
|
+
* `ensureBrowserLaunched` options. Used by the normal launch in `src/index.ts`
|
|
10
|
+
* (`getContext`) and by the Opera browser relaunch in `opera/browserFlags.ts`.
|
|
11
|
+
*
|
|
12
|
+
* `index.ts` and `browserFlags.ts` used to build these options independently,
|
|
13
|
+
* so a relaunch could quietly omit a launch flag (notably `blocklist` /
|
|
14
|
+
* `allowlist`) and the two could drift apart on an upstream intake. Keep the
|
|
15
|
+
* mapping here so the relaunch path always launches the browser exactly like
|
|
16
|
+
* the normal path does, plus whatever `extraChromeArgs` the caller adds.
|
|
17
|
+
*/
|
|
18
|
+
export function buildLaunchOptions(serverArgs, logFile = undefined, extra = {}) {
|
|
19
|
+
const chromeArgs = [
|
|
20
|
+
...(extra.extraChromeArgs ?? []),
|
|
21
|
+
...(serverArgs.chromeArg ?? []).map(String),
|
|
22
|
+
];
|
|
23
|
+
if (serverArgs.proxyServer) {
|
|
24
|
+
chromeArgs.push(`--proxy-server=${serverArgs.proxyServer}`);
|
|
25
|
+
}
|
|
26
|
+
const blocklist = extra.blocklist ??
|
|
27
|
+
(serverArgs.blockedUrlPattern
|
|
28
|
+
? serverArgs.blockedUrlPattern.map(String)
|
|
29
|
+
: undefined);
|
|
30
|
+
const allowlist = extra.allowlist ??
|
|
31
|
+
(serverArgs.allowedUrlPattern
|
|
32
|
+
? serverArgs.allowedUrlPattern.map(String)
|
|
33
|
+
: undefined);
|
|
34
|
+
return {
|
|
35
|
+
headless: serverArgs.headless,
|
|
36
|
+
executablePath: serverArgs.executablePath,
|
|
37
|
+
channel: serverArgs.channel,
|
|
38
|
+
isolated: serverArgs.isolated ?? false,
|
|
39
|
+
userDataDir: serverArgs.userDataDir,
|
|
40
|
+
logFile,
|
|
41
|
+
viewport: serverArgs.viewport,
|
|
42
|
+
chromeArgs,
|
|
43
|
+
ignoreDefaultChromeArgs: (serverArgs.ignoreDefaultChromeArg ?? []).map(String),
|
|
44
|
+
acceptInsecureCerts: serverArgs.acceptInsecureCerts,
|
|
45
|
+
devtools: extra.devtools ?? serverArgs.experimentalDevtools ?? false,
|
|
46
|
+
enableExtensions: serverArgs.categoryExtensions,
|
|
47
|
+
viaCli: serverArgs.viaCli,
|
|
48
|
+
blocklist,
|
|
49
|
+
allowlist,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=browserLaunch.js.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Opera Software AS.
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Opera's behavioural deltas on top of upstream `chrome-devtools-mcp`.
|
|
8
|
+
*
|
|
9
|
+
* The fork does not collect usage statistics and does not send performance
|
|
10
|
+
* trace URLs to Google's CrUX API. Upstream defaults both to `true`; here they
|
|
11
|
+
* default to `false`, the help text is inverted accordingly, and the MCP
|
|
12
|
+
* server entry point additionally forces them off even if a flag asked for
|
|
13
|
+
* them.
|
|
14
|
+
*/
|
|
15
|
+
import { ENV_NO_USAGE_STATISTICS, MCP_BIN_NAME } from './branding.js';
|
|
16
|
+
/** Upstream default is `true`. */
|
|
17
|
+
export const USAGE_STATISTICS_DEFAULT = false;
|
|
18
|
+
/** Upstream default is `true`. */
|
|
19
|
+
export const PERFORMANCE_CRUX_DEFAULT = false;
|
|
20
|
+
/** Inverted from upstream's "Set to false to disable ..." wording. */
|
|
21
|
+
export const PERFORMANCE_CRUX_DESCRIPTION = 'Set to true to enable sending URLs from performance traces to CrUX API to get field performance data.';
|
|
22
|
+
/** Inverted from upstream's "Set to false to opt-out ..." wording. */
|
|
23
|
+
export const USAGE_STATISTICS_DESCRIPTION = `Set to true to opt-in to usage statistics collection. Google collects usage data to improve the tool, handled under the Google Privacy Policy (https://policies.google.com/privacy). This is independent from Chrome browser metrics. Disabled if \`${ENV_NO_USAGE_STATISTICS}\` or \`CI\` env variables are set.`;
|
|
24
|
+
/**
|
|
25
|
+
* Hard-off switch applied by the MCP server entry point after argument
|
|
26
|
+
* parsing. Defaults alone are not enough: a client could pass
|
|
27
|
+
* `--usageStatistics` or `--performanceCrux` explicitly.
|
|
28
|
+
*/
|
|
29
|
+
export function enforceTelemetryPolicy(args) {
|
|
30
|
+
if (args.usageStatistics || args.performanceCrux) {
|
|
31
|
+
console.error('Warning: usage statistics or CrUX were enabled via flags — forcing off.');
|
|
32
|
+
}
|
|
33
|
+
args.usageStatistics = false;
|
|
34
|
+
args.performanceCrux = false;
|
|
35
|
+
}
|
|
36
|
+
/** Branded replacement for upstream's browser-exposure disclaimer. */
|
|
37
|
+
export const BROWSER_EXPOSURE_DISCLAIMER = `${MCP_BIN_NAME} exposes content of the browser instance to the MCP clients allowing them to inspect,
|
|
38
|
+
debug, and modify any data in the browser or DevTools.
|
|
39
|
+
Avoid sharing sensitive or personal information that you do not want to share with MCP clients.`;
|
|
40
|
+
/**
|
|
41
|
+
* Upstream prints a Google usage-statistics opt-out paragraph from
|
|
42
|
+
* `logDisclaimers`. Opera never collects them, so the paragraph is suppressed.
|
|
43
|
+
*
|
|
44
|
+
* A function rather than a `false` constant so that upstream's block in
|
|
45
|
+
* `src/index.ts` stays live code and merges untouched.
|
|
46
|
+
*/
|
|
47
|
+
export function showUsageStatisticsDisclaimer() {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Opera Norway AS. All rights reserved.
|
|
4
|
+
*
|
|
5
|
+
* This file is an original work developed by Opera.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Opera's AI service worker may not be running yet when the first CDP command
|
|
9
|
+
* arrives, so dispatches are retried with a fixed backoff.
|
|
10
|
+
*
|
|
11
|
+
* Exposed as a mutable object so tests can drive the retry loop without waiting
|
|
12
|
+
* on real time. Faking timers is not a workable alternative here: sinon's fake
|
|
13
|
+
* clock replaces the globals `node:test` uses to schedule subtests, which
|
|
14
|
+
* silently drops whole suites from the run.
|
|
15
|
+
*/
|
|
16
|
+
export const serviceWorkerRetryPolicy = {
|
|
17
|
+
maxAttempts: 5,
|
|
18
|
+
delayMs: 2500,
|
|
19
|
+
};
|
|
20
|
+
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
21
|
+
export async function withServiceWorkerRetry(fn) {
|
|
22
|
+
const { maxAttempts, delayMs } = serviceWorkerRetryPolicy;
|
|
23
|
+
let lastError;
|
|
24
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
25
|
+
try {
|
|
26
|
+
return await fn();
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
lastError = e;
|
|
30
|
+
if (attempt < maxAttempts - 1) {
|
|
31
|
+
await sleep(delayMs);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
throw lastError;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=serviceWorkerRetry.js.map
|