opera-devtools-mcp 0.6.1 → 0.8.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 +1 -1
- package/build/src/McpContext.js +78 -41
- package/build/src/McpPage.js +5 -1
- package/build/src/ToolHandler.js +54 -76
- package/build/src/bin/chrome-devtools-mcp-main.js +4 -4
- package/build/src/bin/chrome-devtools.js +56 -124
- package/build/src/bin/opera-browser-cli.js +102 -0
- package/build/src/bin/opera-devtools-cli-options.js +1 -1
- package/build/src/bin/opera-devtools-mcp-cli-options.js +1 -1
- package/build/src/bin/opera-devtools-mcp.js +20 -1
- package/build/src/browser.js +23 -25
- package/build/src/config/browser-options.js +126 -0
- package/build/src/config/category-options.js +81 -0
- package/build/src/{bin/chrome-devtools-cli-options.js → config/cli-options.js} +368 -26
- package/build/src/{bin/chrome-devtools-mcp-cli-options.js → config/mcp-options.js} +143 -164
- package/build/src/daemon/client.js +55 -40
- package/build/src/daemon/daemon.js +62 -39
- package/build/src/daemon/utils.js +6 -0
- package/build/src/devtools/DevtoolsUtils.js +27 -21
- package/build/src/formatters/NetworkFormatter.js +5 -2
- package/build/src/index.js +166 -100
- package/build/src/opera/branding.js +4 -2
- package/build/src/opera/browserActivity.js +62 -0
- package/build/src/opera/browserCleanup.js +123 -0
- package/build/src/opera/browserErrors.js +66 -0
- package/build/src/opera/browserFlags.js +184 -38
- package/build/src/opera/browserTarget.js +513 -0
- package/build/src/opera/cdpErrors.js +391 -0
- package/build/src/opera/cliCommands.js +378 -0
- package/build/src/opera/cliOutput.js +284 -0
- package/build/src/opera/compactSnapshot.js +525 -0
- package/build/src/opera/config.js +166 -0
- package/build/src/opera/daemonLifecycle.js +257 -0
- package/build/src/opera/daemonLog.js +103 -0
- package/build/src/opera/daemonPidFile.js +83 -0
- package/build/src/opera/daemonShutdown.js +66 -0
- package/build/src/opera/daemonSocket.js +87 -0
- package/build/src/opera/daemonStreaming.js +130 -0
- package/build/src/opera/daemonToolCall.js +26 -0
- package/build/src/opera/detect.js +114 -0
- package/build/src/opera/doctor.js +317 -0
- package/build/src/opera/envConfig.js +229 -0
- package/build/src/opera/launcherNotice.js +116 -0
- package/build/src/opera/legacyBridgeCleanup.js +297 -0
- package/build/src/opera/logs.js +133 -0
- package/build/src/opera/mcpServerSupervisor.js +128 -0
- package/build/src/opera/migrationShared.js +164 -0
- package/build/src/opera/operaPages.js +56 -0
- package/build/src/opera/pageIdRouting.js +35 -0
- package/build/src/opera/pageRecovery.js +53 -0
- package/build/src/opera/profile.js +270 -0
- package/build/src/opera/refArgs.js +36 -0
- package/build/src/opera/serviceWorkerRetry.js +46 -4
- package/build/src/opera/setup.js +290 -0
- package/build/src/opera/skills/SKILL.md +160 -0
- package/build/src/opera/streamingTools.js +73 -0
- package/build/src/opera/suggestions.js +67 -0
- package/build/src/opera/toolHandlerHooks.js +25 -1
- package/build/src/opera/tools/opera.js +107 -38
- package/build/src/opera/urlResolver.js +69 -0
- package/build/src/opera/webStorageWarning.js +92 -0
- package/build/src/processors/HeapSnapshotManager.js +12 -0
- package/build/src/telemetry/ClearcutLogger.js +19 -6
- package/build/src/telemetry/transformation.js +4 -0
- package/build/src/telemetry/types.js +4 -0
- package/build/src/third_party/THIRD_PARTY_NOTICES +5 -5
- package/build/src/third_party/bundled-packages.json +3 -3
- package/build/src/third_party/devtools-formatter-worker.js +23 -0
- package/build/src/third_party/devtools-heap-snapshot-worker.js +101 -20
- package/build/src/third_party/index.js +15460 -14256
- package/build/src/third_party/issue-descriptions/federatedAuthRequestAccountsBlockedByConnectionAllowlist.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestConfigBlockedByConnectionAllowlist.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestIdTokenBlockedByConnectionAllowlist.md +1 -0
- package/build/src/third_party/issue-descriptions/federatedAuthRequestWellKnownBlockedByConnectionAllowlist.md +1 -0
- package/build/src/tools/ToolDefinition.js +15 -0
- package/build/src/tools/categories.js +0 -6
- package/build/src/tools/comments.js +90 -0
- package/build/src/tools/console.js +1 -1
- package/build/src/tools/emulation.js +1 -1
- package/build/src/tools/extensions.js +1 -1
- package/build/src/tools/memory.js +60 -6
- package/build/src/tools/network.js +2 -2
- package/build/src/tools/pages.js +26 -15
- package/build/src/tools/performance.js +4 -3
- package/build/src/tools/screencast.js +3 -2
- package/build/src/tools/screenshot.js +39 -8
- package/build/src/tools/script.js +17 -4
- package/build/src/tools/slim/tools.js +41 -33
- package/build/src/tools/snapshot.js +1 -1
- package/build/src/tools/tools.js +2 -0
- package/build/src/utils/WaitForHelper.js +12 -1
- package/build/src/utils/bytes.js +105 -0
- package/build/src/utils/url.js +79 -0
- package/build/src/version.js +1 -1
- package/package.json +29 -8
- package/build/src/bin/opera-devtools.js +0 -10
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { zod } from '../../third_party/index.js';
|
|
7
7
|
import { ToolCategory } from '../categories.js';
|
|
8
8
|
import { definePageTool } from '../ToolDefinition.js';
|
|
9
|
+
import { validateUrl } from '../../utils/url.js';
|
|
9
10
|
export const screenshot = definePageTool({
|
|
10
11
|
name: 'screenshot',
|
|
11
12
|
description: `Takes a screenshot`,
|
|
@@ -27,40 +28,46 @@ export const screenshot = definePageTool({
|
|
|
27
28
|
response.appendResponseLine(filepath);
|
|
28
29
|
},
|
|
29
30
|
});
|
|
30
|
-
export const navigate = definePageTool({
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
31
|
+
export const navigate = definePageTool(args => {
|
|
32
|
+
return {
|
|
33
|
+
name: 'navigate',
|
|
34
|
+
description: `Loads a URL`,
|
|
35
|
+
annotations: {
|
|
36
|
+
category: ToolCategory.NAVIGATION,
|
|
37
|
+
readOnlyHint: false,
|
|
38
|
+
},
|
|
39
|
+
schema: {
|
|
40
|
+
url: zod.string().describe('URL to navigate to'),
|
|
41
|
+
},
|
|
42
|
+
blockedByDialog: false,
|
|
43
|
+
verifyFilesSchema: {},
|
|
44
|
+
handler: async (request, response) => {
|
|
45
|
+
validateUrl(request.params.url, {
|
|
46
|
+
javascriptEvaluation: args?.javascriptEvaluation,
|
|
47
|
+
categoryExtensions: args?.categoryExtensions,
|
|
48
|
+
});
|
|
49
|
+
const page = request.page;
|
|
50
|
+
const options = {
|
|
51
|
+
timeout: 30_000,
|
|
52
|
+
};
|
|
53
|
+
const dialogHandler = (dialog) => {
|
|
54
|
+
if (dialog.type() === 'beforeunload') {
|
|
55
|
+
response.appendResponseLine(`Accepted a beforeunload dialog.`);
|
|
56
|
+
void dialog.accept();
|
|
57
|
+
// We are not going to report the dialog like regular dialogs.
|
|
58
|
+
page.clearDialog();
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
page.pptrPage.on('dialog', dialogHandler);
|
|
62
|
+
try {
|
|
63
|
+
await page.pptrPage.goto(request.params.url, options);
|
|
64
|
+
response.appendResponseLine(`Navigated to ${page.pptrPage.url()}.`);
|
|
53
65
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
finally {
|
|
61
|
-
page.pptrPage.off('dialog', dialogHandler);
|
|
62
|
-
}
|
|
63
|
-
},
|
|
66
|
+
finally {
|
|
67
|
+
page.pptrPage.off('dialog', dialogHandler);
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
};
|
|
64
71
|
});
|
|
65
72
|
export const evaluate = definePageTool({
|
|
66
73
|
name: 'evaluate',
|
|
@@ -68,6 +75,7 @@ export const evaluate = definePageTool({
|
|
|
68
75
|
annotations: {
|
|
69
76
|
category: ToolCategory.DEBUGGING,
|
|
70
77
|
readOnlyHint: false,
|
|
78
|
+
conditions: ['javascriptEvaluation'],
|
|
71
79
|
},
|
|
72
80
|
schema: {
|
|
73
81
|
script: zod.string().describe(`JS script to run on the page`),
|
|
@@ -8,7 +8,7 @@ import { ToolCategory } from './categories.js';
|
|
|
8
8
|
import { definePageTool, timeoutSchema } from './ToolDefinition.js';
|
|
9
9
|
export const takeSnapshot = definePageTool({
|
|
10
10
|
name: 'take_snapshot',
|
|
11
|
-
description: `Take a text snapshot of the
|
|
11
|
+
description: `Take a text snapshot of the target page based on the a11y tree. The snapshot lists page elements along with a unique
|
|
12
12
|
identifier (uid). Always use the latest snapshot. Prefer taking a snapshot over taking a screenshot. The snapshot indicates the element selected
|
|
13
13
|
in the DevTools Elements panel (if any).`,
|
|
14
14
|
annotations: {
|
package/build/src/tools/tools.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* Modified by Opera Software AS.
|
|
7
7
|
*/
|
|
8
8
|
import * as operaTools from '../opera/tools/opera.js';
|
|
9
|
+
import * as commentsTools from './comments.js';
|
|
9
10
|
import * as consoleTools from './console.js';
|
|
10
11
|
import * as emulationTools from './emulation.js';
|
|
11
12
|
import * as extensionTools from './extensions.js';
|
|
@@ -27,6 +28,7 @@ export const createTools = (args) => {
|
|
|
27
28
|
const rawTools = args.slim
|
|
28
29
|
? Object.values(slimTools)
|
|
29
30
|
: [
|
|
31
|
+
...(args.devtoolsComments ? Object.values(commentsTools) : []),
|
|
30
32
|
...Object.values(consoleTools),
|
|
31
33
|
...Object.values(emulationTools),
|
|
32
34
|
...Object.values(extensionTools),
|
|
@@ -187,7 +187,7 @@ export class WaitForHelper {
|
|
|
187
187
|
});
|
|
188
188
|
// A scoped AbortController used to clean up navigation probe listeners.
|
|
189
189
|
// When aborted (either after navigation detection finishes or if this.#abortController
|
|
190
|
-
// aborts), it removes
|
|
190
|
+
// aborts), it removes CDP navigation probe listeners and automatically
|
|
191
191
|
// detaches the abort listener from this.#abortController.signal.
|
|
192
192
|
const navigationAbortController = new AbortController();
|
|
193
193
|
const navigationStartedResolvers = Promise.withResolvers();
|
|
@@ -201,11 +201,22 @@ export class WaitForHelper {
|
|
|
201
201
|
}
|
|
202
202
|
navigationStartedResolvers.resolve(true);
|
|
203
203
|
};
|
|
204
|
+
const requestedNavigationListener = (event) => {
|
|
205
|
+
if (event.frameId === this.#page.mainFrame()._id) {
|
|
206
|
+
navigationStartedResolvers.resolve(true);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
204
209
|
this.#page._client().on('Page.frameStartedNavigating', navigationListener);
|
|
210
|
+
this.#page
|
|
211
|
+
._client()
|
|
212
|
+
.on('Page.frameRequestedNavigation', requestedNavigationListener);
|
|
205
213
|
navigationAbortController.signal.addEventListener('abort', () => {
|
|
206
214
|
this.#page
|
|
207
215
|
._client()
|
|
208
216
|
.off('Page.frameStartedNavigating', navigationListener);
|
|
217
|
+
this.#page
|
|
218
|
+
._client()
|
|
219
|
+
.off('Page.frameRequestedNavigation', requestedNavigationListener);
|
|
209
220
|
});
|
|
210
221
|
this.#abortController.signal.addEventListener('abort', () => {
|
|
211
222
|
navigationStartedResolvers.resolve(false);
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { zod } from '../third_party/index.js';
|
|
7
|
+
const BYTE_UNITS = {
|
|
8
|
+
b: 1,
|
|
9
|
+
byte: 1,
|
|
10
|
+
bytes: 1,
|
|
11
|
+
k: 1000,
|
|
12
|
+
kb: 1000,
|
|
13
|
+
kib: 1024,
|
|
14
|
+
m: 1000 * 1000,
|
|
15
|
+
mb: 1000 * 1000,
|
|
16
|
+
mib: 1024 * 1024,
|
|
17
|
+
g: 1000 * 1000 * 1000,
|
|
18
|
+
gb: 1000 * 1000 * 1000,
|
|
19
|
+
gib: 1024 * 1024 * 1024,
|
|
20
|
+
t: 1000 * 1000 * 1000 * 1000,
|
|
21
|
+
tb: 1000 * 1000 * 1000 * 1000,
|
|
22
|
+
tib: 1024 * 1024 * 1024 * 1024,
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Parses a byte size string (e.g. "1M", "1MB", "500KB", "1.5GB", "1024") into bytes.
|
|
26
|
+
*/
|
|
27
|
+
export function parseByteSize(value) {
|
|
28
|
+
const trimmed = value.trim();
|
|
29
|
+
if (trimmed === '') {
|
|
30
|
+
throw new Error(`Invalid byte size: "${value}"`);
|
|
31
|
+
}
|
|
32
|
+
const match = trimmed.match(/^(\d+(?:\.\d+)?)\s*([a-zA-Z]+)?$/);
|
|
33
|
+
if (!match) {
|
|
34
|
+
throw new Error(`Invalid byte size format: "${value}". Expected a number or format like "1024", "1M", "1MB", "1G", "1GB".`);
|
|
35
|
+
}
|
|
36
|
+
const numMatch = match[1];
|
|
37
|
+
if (numMatch === undefined) {
|
|
38
|
+
throw new Error(`Invalid byte size: "${value}"`);
|
|
39
|
+
}
|
|
40
|
+
const num = Number(numMatch);
|
|
41
|
+
if (!Number.isFinite(num) || num < 0) {
|
|
42
|
+
throw new Error(`Invalid byte size: "${value}"`);
|
|
43
|
+
}
|
|
44
|
+
const unitMatch = match[2];
|
|
45
|
+
if (unitMatch === undefined) {
|
|
46
|
+
return Math.round(num);
|
|
47
|
+
}
|
|
48
|
+
const unit = unitMatch.toLowerCase();
|
|
49
|
+
const multiplier = BYTE_UNITS[unit];
|
|
50
|
+
if (multiplier === undefined) {
|
|
51
|
+
throw new Error(`Unknown unit "${unitMatch}" in "${value}". Supported units: B, KB, KiB, MB, MiB, GB, GiB, TB, TiB.`);
|
|
52
|
+
}
|
|
53
|
+
const bytes = Math.round(num * multiplier);
|
|
54
|
+
if (!Number.isFinite(bytes)) {
|
|
55
|
+
throw new Error(`Invalid byte size: "${value}"`);
|
|
56
|
+
}
|
|
57
|
+
return bytes;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Parses an inclusive byte-size range (e.g. "1MB", "1MB-2MB", "-1MB", "1MB-").
|
|
61
|
+
*/
|
|
62
|
+
export function parseByteSizeRange(value) {
|
|
63
|
+
const trimmed = value.trim();
|
|
64
|
+
if (!trimmed.includes('-')) {
|
|
65
|
+
return { min: parseByteSize(trimmed), max: undefined };
|
|
66
|
+
}
|
|
67
|
+
const parts = trimmed.split('-');
|
|
68
|
+
if (parts.length !== 2) {
|
|
69
|
+
throw new Error(`Invalid byte size range: "${value}". Expected a size or range like "1MB", "1MB-2MB", "-1MB", or "1MB-".`);
|
|
70
|
+
}
|
|
71
|
+
const minValue = parts[0];
|
|
72
|
+
const maxValue = parts[1];
|
|
73
|
+
if (minValue === undefined || maxValue === undefined) {
|
|
74
|
+
throw new Error(`Invalid byte size range: "${value}"`);
|
|
75
|
+
}
|
|
76
|
+
const minText = minValue.trim();
|
|
77
|
+
const maxText = maxValue.trim();
|
|
78
|
+
if (minText === '' && maxText === '') {
|
|
79
|
+
throw new Error(`Invalid byte size range: "${value}". At least one bound is required.`);
|
|
80
|
+
}
|
|
81
|
+
const min = minText === '' ? 0 : parseByteSize(minText);
|
|
82
|
+
const max = maxText === '' ? undefined : parseByteSize(maxText);
|
|
83
|
+
if (max !== undefined && min > max) {
|
|
84
|
+
throw new Error(`Invalid byte size range: "${value}". The lower bound must not exceed the upper bound.`);
|
|
85
|
+
}
|
|
86
|
+
return { min, max };
|
|
87
|
+
}
|
|
88
|
+
export function byteSizeRangeSchema(description) {
|
|
89
|
+
return zod
|
|
90
|
+
.string()
|
|
91
|
+
.transform((value, context) => {
|
|
92
|
+
try {
|
|
93
|
+
return parseByteSizeRange(value);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
context.addIssue({
|
|
97
|
+
code: zod.ZodIssueCode.custom,
|
|
98
|
+
message: error instanceof Error ? error.message : 'Invalid byte size range',
|
|
99
|
+
});
|
|
100
|
+
return zod.NEVER;
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
.describe(description);
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=bytes.js.map
|
package/build/src/utils/url.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Copyright 2026 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
+
import { isOperaStartPage } from '../opera/operaPages.js';
|
|
6
7
|
const ALLOWED_PROTOCOLS = new Set(['http:', 'https:', 'ws:', 'wss:']);
|
|
7
8
|
const IPV4_LOOPBACK_REGEX = /^127(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d\d?)){3}$/;
|
|
8
9
|
/**
|
|
@@ -40,4 +41,82 @@ export function isLocalhost(url) {
|
|
|
40
41
|
}
|
|
41
42
|
return false;
|
|
42
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Determines whether a URL is allowed for navigation and page tracking.
|
|
46
|
+
*
|
|
47
|
+
* Disallowed schemes:
|
|
48
|
+
* - chrome: (except chrome://newtab/ and chrome://inspect*)
|
|
49
|
+
* - chrome-untrusted:
|
|
50
|
+
* - chrome-extension: (unless categoryExtensions is true)
|
|
51
|
+
*
|
|
52
|
+
* @param url The URL string to test.
|
|
53
|
+
* @param options Configuration specifying whether extensions are enabled.
|
|
54
|
+
* @returns true if the URL is allowed.
|
|
55
|
+
*/
|
|
56
|
+
export function isAllowedUrl(url, options) {
|
|
57
|
+
let parsed;
|
|
58
|
+
try {
|
|
59
|
+
parsed = new URL(url);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
if (parsed.protocol === 'chrome:') {
|
|
65
|
+
const host = parsed.hostname.toLowerCase();
|
|
66
|
+
const path = parsed.pathname.toLowerCase();
|
|
67
|
+
if (host === 'newtab' || host === 'new-tab-page' || host === 'inspect') {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
if (host === '' &&
|
|
71
|
+
(path === 'newtab' ||
|
|
72
|
+
path === 'new-tab-page' ||
|
|
73
|
+
path.startsWith('inspect'))) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
// Opera's start page, which is where every tab the user opens by hand
|
|
77
|
+
// begins; see `opera/operaPages.ts` for why it cannot be filtered out.
|
|
78
|
+
if (isOperaStartPage(parsed)) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (parsed.protocol === 'chrome:' ||
|
|
83
|
+
parsed.protocol === 'chrome-untrusted:') {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
if (!options.categoryExtensions && parsed.protocol === 'chrome-extension:') {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
const DISALLOWED_PROTOCOLS = new Set(['javascript:', 'data:', 'vbscript:']);
|
|
92
|
+
/**
|
|
93
|
+
* Validates a URL string by parsing it with `new URL` and checking for disallowed protocols and restricted schemes.
|
|
94
|
+
*
|
|
95
|
+
* @param url The URL string to validate.
|
|
96
|
+
* @param options Options object containing javascriptEvaluation and categoryExtensions.
|
|
97
|
+
* @returns The parsed URL.
|
|
98
|
+
* @throws Error if the URL does not parse with `new URL`, or if JavaScript evaluation is disabled and a disallowed URL is passed,
|
|
99
|
+
* or if navigating to a restricted scheme.
|
|
100
|
+
*/
|
|
101
|
+
export function validateUrl(url, options) {
|
|
102
|
+
const { javascriptEvaluation, categoryExtensions } = options;
|
|
103
|
+
let parsed;
|
|
104
|
+
try {
|
|
105
|
+
parsed = new URL(url);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
throw new Error(`Invalid URL: "${url}". URLs must be valid according to the URL standard.`);
|
|
109
|
+
}
|
|
110
|
+
if (javascriptEvaluation === false &&
|
|
111
|
+
DISALLOWED_PROTOCOLS.has(parsed.protocol)) {
|
|
112
|
+
throw new Error(`Navigating to ${parsed.protocol} URLs is not allowed when JavaScript evaluation is disabled.`);
|
|
113
|
+
}
|
|
114
|
+
if (!isAllowedUrl(url, { categoryExtensions })) {
|
|
115
|
+
if (parsed.protocol === 'chrome-extension:') {
|
|
116
|
+
throw new Error(`Navigating to chrome-extension: URLs is not allowed without --categoryExtensions.`);
|
|
117
|
+
}
|
|
118
|
+
throw new Error(`Navigating to ${parsed.protocol} URLs is not allowed.`);
|
|
119
|
+
}
|
|
120
|
+
return parsed;
|
|
121
|
+
}
|
|
43
122
|
//# sourceMappingURL=url.js.map
|
package/build/src/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opera-devtools-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "MCP server for Opera DevTools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"opera-devtools-mcp": "./build/src/bin/opera-devtools-mcp.js"
|
|
8
|
-
"opera-devtools": "./build/src/bin/opera-devtools.js"
|
|
7
|
+
"opera-devtools-mcp": "./build/src/bin/opera-devtools-mcp.js"
|
|
9
8
|
},
|
|
10
9
|
"main": "./build/src/index.js",
|
|
11
10
|
"scripts": {
|
|
@@ -13,10 +12,13 @@
|
|
|
13
12
|
"clean": "node -e \"require('fs').rmSync('build', {recursive: true, force: true})\"",
|
|
14
13
|
"bundle": "npm run clean && npm run build && rollup -c rollup.config.js && node -e \"require('fs').rmSync('build/node_modules', {recursive: true, force: true})\" && node scripts/append-lighthouse-notices.ts",
|
|
15
14
|
"build": "tsc && node scripts/post-build.ts",
|
|
15
|
+
"prepublishOnly": "npm run bundle",
|
|
16
16
|
"typecheck": "tsc --noEmit",
|
|
17
17
|
"format": "eslint --cache --fix . && prettier --write --cache .",
|
|
18
18
|
"check-format": "eslint --cache . && prettier --check --cache .;",
|
|
19
19
|
"gen": "npm run build && npm run docs:generate && npm run cli:generate && npm run update-metrics && npm run format",
|
|
20
|
+
"link": "node scripts/link.ts",
|
|
21
|
+
"link:cli": "node scripts/link.ts --with-cli",
|
|
20
22
|
"docs:generate": "node scripts/generate-docs.ts",
|
|
21
23
|
"start": "npm run build && node build/src/bin/opera-devtools-mcp.js",
|
|
22
24
|
"start-debug": "DEBUG=mcp:* DEBUG_COLORS=false npm run build && node build/src/bin/opera-devtools-mcp.js",
|
|
@@ -35,7 +37,9 @@
|
|
|
35
37
|
"eval": "npm run build && node scripts/eval_gemini.ts",
|
|
36
38
|
"count-tokens": "node scripts/count_tokens.ts",
|
|
37
39
|
"profile": "npm run build && node scripts/profile/profile_mcp.ts",
|
|
38
|
-
"test:memory": "npm run build && node scripts/profile/profile_mcp.ts --threshold-percentage 1"
|
|
40
|
+
"test:memory": "npm run build && node scripts/profile/profile_mcp.ts --threshold-percentage 1",
|
|
41
|
+
"test:stress": "npm run build && STRESS_ALLOW_PROCESS_KILLS=true node --import ./build/tests/setup.js --test --test-force-exit --test-timeout=600000 build/tests/stress/runner.test.js",
|
|
42
|
+
"test:stress:docker": "node scripts/stress-docker.js"
|
|
39
43
|
},
|
|
40
44
|
"files": [
|
|
41
45
|
"build/src",
|
|
@@ -51,6 +55,9 @@
|
|
|
51
55
|
},
|
|
52
56
|
"homepage": "https://github.com/operasoftware/opera-devtools-mcp#readme",
|
|
53
57
|
"mcpName": "io.github.operasoftware/opera-devtools-mcp",
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"@toon-format/toon": "^4.1.0"
|
|
60
|
+
},
|
|
54
61
|
"devDependencies": {
|
|
55
62
|
"@blackwell-systems/gcf": "^2.2.2",
|
|
56
63
|
"@eslint/js": "^10.0.1",
|
|
@@ -60,7 +67,6 @@
|
|
|
60
67
|
"@rollup/plugin-json": "^6.1.0",
|
|
61
68
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
62
69
|
"@stylistic/eslint-plugin": "^5.4.0",
|
|
63
|
-
"@toon-format/toon": "^4.1.0",
|
|
64
70
|
"@types/filesystem": "^0.0.36",
|
|
65
71
|
"@types/node": "^26.0.0",
|
|
66
72
|
"@types/semver": "^7.7.1",
|
|
@@ -68,15 +74,15 @@
|
|
|
68
74
|
"@types/yargs": "^17.0.33",
|
|
69
75
|
"@typescript-eslint/eslint-plugin": "^8.43.0",
|
|
70
76
|
"@typescript-eslint/parser": "^8.43.0",
|
|
71
|
-
"core-js": "3.
|
|
77
|
+
"core-js": "3.50.0",
|
|
72
78
|
"eslint": "^10.7.0",
|
|
73
79
|
"eslint-import-resolver-typescript": "^4.4.4",
|
|
74
80
|
"eslint-plugin-import": "^2.32.0",
|
|
75
81
|
"globals": "^17.0.0",
|
|
76
82
|
"lighthouse": "13.4.1",
|
|
77
83
|
"prettier": "^3.6.2",
|
|
78
|
-
"puppeteer": "25.
|
|
79
|
-
"rollup": "4.
|
|
84
|
+
"puppeteer": "25.10.0",
|
|
85
|
+
"rollup": "4.63.1",
|
|
80
86
|
"rollup-plugin-cleanup": "^3.2.1",
|
|
81
87
|
"rollup-plugin-license": "^3.6.0",
|
|
82
88
|
"semver": "^7.7.4",
|
|
@@ -86,6 +92,14 @@
|
|
|
86
92
|
"yargs": "18.1.0",
|
|
87
93
|
"zod": "^3.25.76"
|
|
88
94
|
},
|
|
95
|
+
"peerDependencies": {
|
|
96
|
+
"@blackwell-systems/gcf": "^2.2.2"
|
|
97
|
+
},
|
|
98
|
+
"peerDependenciesMeta": {
|
|
99
|
+
"@blackwell-systems/gcf": {
|
|
100
|
+
"optional": true
|
|
101
|
+
}
|
|
102
|
+
},
|
|
89
103
|
"engines": {
|
|
90
104
|
"node": "^20.19.0 || ^22.12.0 || >=23"
|
|
91
105
|
},
|
|
@@ -94,5 +108,12 @@
|
|
|
94
108
|
"eslint": "$eslint"
|
|
95
109
|
},
|
|
96
110
|
"puppeteer-core": "$puppeteer"
|
|
111
|
+
},
|
|
112
|
+
"allowScripts": {
|
|
113
|
+
"core-js": false,
|
|
114
|
+
"@google/genai": false,
|
|
115
|
+
"protobufjs": false,
|
|
116
|
+
"unrs-resolver": false,
|
|
117
|
+
"puppeteer": true
|
|
97
118
|
}
|
|
98
119
|
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* @license
|
|
4
|
-
* Copyright 2026 Opera Software AS.
|
|
5
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
-
*/
|
|
7
|
-
// Opera-named bin entry (see `package.json` `bin`). The implementation lives in
|
|
8
|
-
// the upstream-owned file and is branded via `src/opera/branding.ts`.
|
|
9
|
-
import './chrome-devtools.js';
|
|
10
|
-
//# sourceMappingURL=opera-devtools.js.map
|