@cotestdev/mcp_playwright 0.0.12 → 0.0.15
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/lib/mcp/browser/browserContextFactory.js +49 -13
- package/lib/mcp/browser/browserServerBackend.js +5 -2
- package/lib/mcp/browser/config.js +95 -23
- package/lib/mcp/browser/context.js +28 -3
- package/lib/mcp/browser/response.js +240 -57
- package/lib/mcp/browser/sessionLog.js +1 -1
- package/lib/mcp/browser/tab.js +96 -69
- package/lib/mcp/browser/tools/common.js +8 -8
- package/lib/mcp/browser/tools/console.js +6 -3
- package/lib/mcp/browser/tools/dialogs.js +13 -13
- package/lib/mcp/browser/tools/evaluate.js +9 -20
- package/lib/mcp/browser/tools/files.js +10 -5
- package/lib/mcp/browser/tools/form.js +11 -22
- package/lib/mcp/browser/tools/install.js +3 -3
- package/lib/mcp/browser/tools/keyboard.js +12 -12
- package/lib/mcp/browser/tools/mouse.js +14 -14
- package/lib/mcp/browser/tools/navigate.js +5 -5
- package/lib/mcp/browser/tools/network.js +16 -5
- package/lib/mcp/browser/tools/pdf.js +7 -18
- package/lib/mcp/browser/tools/runCode.js +77 -0
- package/lib/mcp/browser/tools/screenshot.js +44 -33
- package/lib/mcp/browser/tools/snapshot.js +42 -33
- package/lib/mcp/browser/tools/tabs.js +7 -10
- package/lib/mcp/browser/tools/tool.js +8 -7
- package/lib/mcp/browser/tools/tracing.js +4 -4
- package/lib/mcp/browser/tools/utils.js +50 -52
- package/lib/mcp/browser/tools/verify.js +23 -34
- package/lib/mcp/browser/tools/wait.js +6 -6
- package/lib/mcp/browser/tools.js +4 -3
- package/lib/mcp/cli.js +17 -17
- package/lib/mcp/extension/cdpRelay.js +1 -1
- package/lib/mcp/extension/extensionContextFactory.js +4 -3
- package/lib/mcp/log.js +2 -2
- package/lib/mcp/program.js +21 -29
- package/lib/mcp/sdk/exports.js +1 -5
- package/lib/mcp/sdk/http.js +37 -50
- package/lib/mcp/sdk/server.js +61 -9
- package/lib/mcp/sdk/tool.js +5 -4
- package/lib/mcp/test/browserBackend.js +67 -61
- package/lib/mcp/test/generatorTools.js +122 -0
- package/lib/mcp/test/plannerTools.js +144 -0
- package/lib/mcp/test/seed.js +82 -0
- package/lib/mcp/test/streams.js +10 -7
- package/lib/mcp/test/testBackend.js +44 -24
- package/lib/mcp/test/testContext.js +243 -14
- package/lib/mcp/test/testTools.js +23 -109
- package/lib/util.js +12 -6
- package/package.json +1 -1
|
@@ -21,10 +21,9 @@ __export(keyboard_exports, {
|
|
|
21
21
|
default: () => keyboard_default
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(keyboard_exports);
|
|
24
|
-
var
|
|
24
|
+
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
|
25
25
|
var import_tool = require("./tool");
|
|
26
26
|
var import_snapshot = require("./snapshot");
|
|
27
|
-
var import_utils = require("./utils");
|
|
28
27
|
var import_common = require("./common");
|
|
29
28
|
const pressKey = (0, import_tool.defineTabTool)({
|
|
30
29
|
capability: "core",
|
|
@@ -33,12 +32,13 @@ const pressKey = (0, import_tool.defineTabTool)({
|
|
|
33
32
|
title: "Press a key",
|
|
34
33
|
description: "Press a key on the keyboard",
|
|
35
34
|
inputSchema: import_common.baseSchema.extend({
|
|
36
|
-
key:
|
|
35
|
+
key: import_mcpBundle.z.string().describe("Name of the key to press or a character to generate, such as `ArrowLeft` or `a`")
|
|
37
36
|
}),
|
|
38
|
-
type: "
|
|
37
|
+
type: "input"
|
|
39
38
|
},
|
|
40
39
|
handle: async (tab, params, response) => {
|
|
41
40
|
response.setIncludeSnapshot();
|
|
41
|
+
response.addCode(`// Press ${params.key}`);
|
|
42
42
|
response.addCode(`await page.keyboard.press('${params.key}');`);
|
|
43
43
|
await tab.waitForCompletion(async () => {
|
|
44
44
|
await tab.page.keyboard.press(params.key);
|
|
@@ -46,9 +46,9 @@ const pressKey = (0, import_tool.defineTabTool)({
|
|
|
46
46
|
}
|
|
47
47
|
});
|
|
48
48
|
const typeSchema = import_snapshot.elementSchema.extend({
|
|
49
|
-
text:
|
|
50
|
-
submit:
|
|
51
|
-
slowly:
|
|
49
|
+
text: import_mcpBundle.z.string().describe("Text to type into the element"),
|
|
50
|
+
submit: import_mcpBundle.z.boolean().optional().describe("Whether to submit entered text (press Enter after)"),
|
|
51
|
+
slowly: import_mcpBundle.z.boolean().optional().describe("Whether to type one character at a time. Useful for triggering key handlers in the page. By default entire text is filled in at once.")
|
|
52
52
|
});
|
|
53
53
|
const type = (0, import_tool.defineTabTool)({
|
|
54
54
|
capability: "core",
|
|
@@ -57,23 +57,23 @@ const type = (0, import_tool.defineTabTool)({
|
|
|
57
57
|
title: "Type text",
|
|
58
58
|
description: "Type text into editable element",
|
|
59
59
|
inputSchema: typeSchema,
|
|
60
|
-
type: "
|
|
60
|
+
type: "input"
|
|
61
61
|
},
|
|
62
62
|
handle: async (tab, params, response) => {
|
|
63
|
-
const locator = await tab.refLocator(params);
|
|
63
|
+
const { locator, resolved } = await tab.refLocator(params);
|
|
64
64
|
const secret = tab.context.lookupSecret(params.text);
|
|
65
65
|
await tab.waitForCompletion(async () => {
|
|
66
66
|
if (params.slowly) {
|
|
67
67
|
response.setIncludeSnapshot();
|
|
68
|
-
response.addCode(`await page.${
|
|
68
|
+
response.addCode(`await page.${resolved}.pressSequentially(${secret.code});`);
|
|
69
69
|
await locator.pressSequentially(secret.value);
|
|
70
70
|
} else {
|
|
71
|
-
response.addCode(`await page.${
|
|
71
|
+
response.addCode(`await page.${resolved}.fill(${secret.code});`);
|
|
72
72
|
await locator.fill(secret.value);
|
|
73
73
|
}
|
|
74
74
|
if (params.submit) {
|
|
75
75
|
response.setIncludeSnapshot();
|
|
76
|
-
response.addCode(`await page.${
|
|
76
|
+
response.addCode(`await page.${resolved}.press('Enter');`);
|
|
77
77
|
await locator.press("Enter");
|
|
78
78
|
}
|
|
79
79
|
});
|
|
@@ -21,10 +21,10 @@ __export(mouse_exports, {
|
|
|
21
21
|
default: () => mouse_default
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(mouse_exports);
|
|
24
|
-
var
|
|
24
|
+
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
|
25
25
|
var import_tool = require("./tool");
|
|
26
|
-
const elementSchema =
|
|
27
|
-
element:
|
|
26
|
+
const elementSchema = import_mcpBundle.z.object({
|
|
27
|
+
element: import_mcpBundle.z.string().describe("Human-readable element description used to obtain permission to interact with the element")
|
|
28
28
|
});
|
|
29
29
|
const mouseMove = (0, import_tool.defineTabTool)({
|
|
30
30
|
capability: "vision",
|
|
@@ -33,10 +33,10 @@ const mouseMove = (0, import_tool.defineTabTool)({
|
|
|
33
33
|
title: "Move mouse",
|
|
34
34
|
description: "Move mouse to a given position",
|
|
35
35
|
inputSchema: elementSchema.extend({
|
|
36
|
-
x:
|
|
37
|
-
y:
|
|
36
|
+
x: import_mcpBundle.z.number().describe("X coordinate"),
|
|
37
|
+
y: import_mcpBundle.z.number().describe("Y coordinate")
|
|
38
38
|
}),
|
|
39
|
-
type: "
|
|
39
|
+
type: "input"
|
|
40
40
|
},
|
|
41
41
|
handle: async (tab, params, response) => {
|
|
42
42
|
response.addCode(`// Move mouse to (${params.x}, ${params.y})`);
|
|
@@ -53,10 +53,10 @@ const mouseClick = (0, import_tool.defineTabTool)({
|
|
|
53
53
|
title: "Click",
|
|
54
54
|
description: "Click left mouse button at a given position",
|
|
55
55
|
inputSchema: elementSchema.extend({
|
|
56
|
-
x:
|
|
57
|
-
y:
|
|
56
|
+
x: import_mcpBundle.z.number().describe("X coordinate"),
|
|
57
|
+
y: import_mcpBundle.z.number().describe("Y coordinate")
|
|
58
58
|
}),
|
|
59
|
-
type: "
|
|
59
|
+
type: "input"
|
|
60
60
|
},
|
|
61
61
|
handle: async (tab, params, response) => {
|
|
62
62
|
response.setIncludeSnapshot();
|
|
@@ -78,12 +78,12 @@ const mouseDrag = (0, import_tool.defineTabTool)({
|
|
|
78
78
|
title: "Drag mouse",
|
|
79
79
|
description: "Drag left mouse button to a given position",
|
|
80
80
|
inputSchema: elementSchema.extend({
|
|
81
|
-
startX:
|
|
82
|
-
startY:
|
|
83
|
-
endX:
|
|
84
|
-
endY:
|
|
81
|
+
startX: import_mcpBundle.z.number().describe("Start X coordinate"),
|
|
82
|
+
startY: import_mcpBundle.z.number().describe("Start Y coordinate"),
|
|
83
|
+
endX: import_mcpBundle.z.number().describe("End X coordinate"),
|
|
84
|
+
endY: import_mcpBundle.z.number().describe("End Y coordinate")
|
|
85
85
|
}),
|
|
86
|
-
type: "
|
|
86
|
+
type: "input"
|
|
87
87
|
},
|
|
88
88
|
handle: async (tab, params, response) => {
|
|
89
89
|
response.setIncludeSnapshot();
|
|
@@ -21,9 +21,9 @@ __export(navigate_exports, {
|
|
|
21
21
|
default: () => navigate_default
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(navigate_exports);
|
|
24
|
-
var
|
|
25
|
-
var import_common = require("./common");
|
|
24
|
+
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
|
26
25
|
var import_tool = require("./tool");
|
|
26
|
+
var import_common = require("./common");
|
|
27
27
|
const navigate = (0, import_tool.defineTool)({
|
|
28
28
|
capability: "core",
|
|
29
29
|
schema: {
|
|
@@ -31,9 +31,9 @@ const navigate = (0, import_tool.defineTool)({
|
|
|
31
31
|
title: "Navigate to a URL",
|
|
32
32
|
description: "Navigate to a URL",
|
|
33
33
|
inputSchema: import_common.baseSchema.extend({
|
|
34
|
-
url:
|
|
34
|
+
url: import_mcpBundle.z.string().describe("The URL to navigate to")
|
|
35
35
|
}),
|
|
36
|
-
type: "
|
|
36
|
+
type: "action"
|
|
37
37
|
},
|
|
38
38
|
handle: async (context, params, response) => {
|
|
39
39
|
const tab = await context.ensureTab();
|
|
@@ -49,7 +49,7 @@ const goBack = (0, import_tool.defineTabTool)({
|
|
|
49
49
|
title: "Go back",
|
|
50
50
|
description: "Go back to the previous page",
|
|
51
51
|
inputSchema: import_common.baseSchema.extend({}),
|
|
52
|
-
type: "
|
|
52
|
+
type: "action"
|
|
53
53
|
},
|
|
54
54
|
handle: async (tab, params, response) => {
|
|
55
55
|
await tab.page.goBack();
|
|
@@ -21,7 +21,7 @@ __export(network_exports, {
|
|
|
21
21
|
default: () => network_default
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(network_exports);
|
|
24
|
-
var
|
|
24
|
+
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
|
25
25
|
var import_tool = require("./tool");
|
|
26
26
|
const requests = (0, import_tool.defineTabTool)({
|
|
27
27
|
capability: "core",
|
|
@@ -29,15 +29,26 @@ const requests = (0, import_tool.defineTabTool)({
|
|
|
29
29
|
name: "browser_network_requests",
|
|
30
30
|
title: "List network requests",
|
|
31
31
|
description: "Returns all network requests since loading the page",
|
|
32
|
-
inputSchema:
|
|
32
|
+
inputSchema: import_mcpBundle.z.object({
|
|
33
|
+
includeStatic: import_mcpBundle.z.boolean().default(false).describe("Whether to include successful static resources like images, fonts, scripts, etc. Defaults to false.")
|
|
34
|
+
}),
|
|
33
35
|
type: "readOnly"
|
|
34
36
|
},
|
|
35
37
|
handle: async (tab, params, response) => {
|
|
36
|
-
const requests2 = tab.requests();
|
|
37
|
-
|
|
38
|
+
const requests2 = await tab.requests();
|
|
39
|
+
for (const request of requests2) {
|
|
40
|
+
const rendered = await renderRequest(request, params.includeStatic);
|
|
41
|
+
if (rendered)
|
|
42
|
+
response.addResult(rendered);
|
|
43
|
+
}
|
|
38
44
|
}
|
|
39
45
|
});
|
|
40
|
-
function renderRequest(request,
|
|
46
|
+
async function renderRequest(request, includeStatic) {
|
|
47
|
+
const response = request._hasResponse ? await request.response() : void 0;
|
|
48
|
+
const isStaticRequest = ["document", "stylesheet", "image", "media", "font", "script", "manifest"].includes(request.resourceType());
|
|
49
|
+
const isSuccessfulRequest = !response || response.status() < 400;
|
|
50
|
+
if (isStaticRequest && isSuccessfulRequest && !includeStatic)
|
|
51
|
+
return void 0;
|
|
41
52
|
const result = [];
|
|
42
53
|
result.push(`[${request.method().toUpperCase()}] ${request.url()}`);
|
|
43
54
|
if (response)
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
6
|
var __export = (target, all) => {
|
|
9
7
|
for (var name in all)
|
|
@@ -17,26 +15,18 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
15
|
}
|
|
18
16
|
return to;
|
|
19
17
|
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
19
|
var pdf_exports = {};
|
|
30
20
|
__export(pdf_exports, {
|
|
31
21
|
default: () => pdf_default
|
|
32
22
|
});
|
|
33
23
|
module.exports = __toCommonJS(pdf_exports);
|
|
34
|
-
var
|
|
24
|
+
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
|
25
|
+
var import_utils = require("playwright-core/lib/utils");
|
|
35
26
|
var import_tool = require("./tool");
|
|
36
|
-
var
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
filename: import_bundle.z.string().optional().describe("File name to save the pdf to. Defaults to `page-{timestamp}.pdf` if not specified.")
|
|
27
|
+
var import_utils2 = require("./utils");
|
|
28
|
+
const pdfSchema = import_mcpBundle.z.object({
|
|
29
|
+
filename: import_mcpBundle.z.string().optional().describe("File name to save the pdf to. Defaults to `page-{timestamp}.pdf` if not specified. Prefer relative file names to stay within the output directory.")
|
|
40
30
|
});
|
|
41
31
|
const pdf = (0, import_tool.defineTabTool)({
|
|
42
32
|
capability: "pdf",
|
|
@@ -48,9 +38,8 @@ const pdf = (0, import_tool.defineTabTool)({
|
|
|
48
38
|
type: "readOnly"
|
|
49
39
|
},
|
|
50
40
|
handle: async (tab, params, response) => {
|
|
51
|
-
const fileName = await
|
|
52
|
-
response.addCode(`await page.pdf(${
|
|
53
|
-
response.addResult(`Saved page as ${fileName}`);
|
|
41
|
+
const fileName = await response.addFile(params.filename ?? (0, import_utils2.dateAsFileName)("pdf"), { origin: "llm", reason: "Page saved as PDF" });
|
|
42
|
+
response.addCode(`await page.pdf(${(0, import_utils.formatObject)({ path: fileName })});`);
|
|
54
43
|
await tab.page.pdf({ path: fileName });
|
|
55
44
|
}
|
|
56
45
|
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var runCode_exports = {};
|
|
30
|
+
__export(runCode_exports, {
|
|
31
|
+
default: () => runCode_default
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(runCode_exports);
|
|
34
|
+
var import_vm = __toESM(require("vm"));
|
|
35
|
+
var import_utils = require("playwright-core/lib/utils");
|
|
36
|
+
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
|
37
|
+
var import_tool = require("./tool");
|
|
38
|
+
const codeSchema = import_mcpBundle.z.object({
|
|
39
|
+
code: import_mcpBundle.z.string().describe(`A JavaScript function containing Playwright code to execute. It will be invoked with a single argument, page, which you can use for any page interaction. For example: \`async (page) => { await page.getByRole('button', { name: 'Submit' }).click(); return await page.title(); }\``)
|
|
40
|
+
});
|
|
41
|
+
const runCode = (0, import_tool.defineTabTool)({
|
|
42
|
+
capability: "core",
|
|
43
|
+
schema: {
|
|
44
|
+
name: "browser_run_code",
|
|
45
|
+
title: "Run Playwright code",
|
|
46
|
+
description: "Run Playwright code snippet",
|
|
47
|
+
inputSchema: codeSchema,
|
|
48
|
+
type: "action"
|
|
49
|
+
},
|
|
50
|
+
handle: async (tab, params, response) => {
|
|
51
|
+
response.setIncludeSnapshot();
|
|
52
|
+
response.addCode(`await (${params.code})(page);`);
|
|
53
|
+
const __end__ = new import_utils.ManualPromise();
|
|
54
|
+
const context = {
|
|
55
|
+
page: tab.page,
|
|
56
|
+
__end__
|
|
57
|
+
};
|
|
58
|
+
import_vm.default.createContext(context);
|
|
59
|
+
await tab.waitForCompletion(async () => {
|
|
60
|
+
const snippet = `(async () => {
|
|
61
|
+
try {
|
|
62
|
+
const result = await (${params.code})(page);
|
|
63
|
+
__end__.resolve(JSON.stringify(result));
|
|
64
|
+
} catch (e) {
|
|
65
|
+
__end__.reject(e);
|
|
66
|
+
}
|
|
67
|
+
})()`;
|
|
68
|
+
await import_vm.default.runInContext(snippet, context);
|
|
69
|
+
const result = await __end__;
|
|
70
|
+
if (typeof result === "string")
|
|
71
|
+
response.addResult(result);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
var runCode_default = [
|
|
76
|
+
runCode
|
|
77
|
+
];
|
|
@@ -28,30 +28,24 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
29
|
var screenshot_exports = {};
|
|
30
30
|
__export(screenshot_exports, {
|
|
31
|
-
default: () => screenshot_default
|
|
31
|
+
default: () => screenshot_default,
|
|
32
|
+
scaleImageToFitMessage: () => scaleImageToFitMessage
|
|
32
33
|
});
|
|
33
34
|
module.exports = __toCommonJS(screenshot_exports);
|
|
34
|
-
var
|
|
35
|
+
var import_fs = __toESM(require("fs"));
|
|
36
|
+
var import_utils = require("playwright-core/lib/utils");
|
|
37
|
+
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
|
38
|
+
var import_utils2 = require("playwright-core/lib/utils");
|
|
39
|
+
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
|
35
40
|
var import_tool = require("./tool");
|
|
36
|
-
var
|
|
37
|
-
var import_utils = require("./utils");
|
|
41
|
+
var import_utils3 = require("./utils");
|
|
38
42
|
var import_common = require("./common");
|
|
39
43
|
const screenshotSchema = import_common.baseSchema.extend({
|
|
40
|
-
type:
|
|
41
|
-
filename:
|
|
42
|
-
element:
|
|
43
|
-
ref:
|
|
44
|
-
fullPage:
|
|
45
|
-
}).refine((data) => {
|
|
46
|
-
return !!data.element === !!data.ref;
|
|
47
|
-
}, {
|
|
48
|
-
message: "Both element and ref must be provided or neither.",
|
|
49
|
-
path: ["ref", "element"]
|
|
50
|
-
}).refine((data) => {
|
|
51
|
-
return !(data.fullPage && (data.element || data.ref));
|
|
52
|
-
}, {
|
|
53
|
-
message: "fullPage cannot be used with element screenshots.",
|
|
54
|
-
path: ["fullPage"]
|
|
44
|
+
type: import_mcpBundle.z.enum(["png", "jpeg"]).default("png").describe("Image format for the screenshot. Default is png."),
|
|
45
|
+
filename: import_mcpBundle.z.string().optional().describe("File name to save the screenshot to. Defaults to `page-{timestamp}.{png|jpeg}` if not specified. Prefer relative file names to stay within the output directory."),
|
|
46
|
+
element: import_mcpBundle.z.string().optional().describe("Human-readable element description used to obtain permission to screenshot the element. If not provided, the screenshot will be taken of viewport. If element is provided, ref must be provided too."),
|
|
47
|
+
ref: import_mcpBundle.z.string().optional().describe("Exact target element reference from the page snapshot. If not provided, the screenshot will be taken of viewport. If ref is provided, element must be provided too."),
|
|
48
|
+
fullPage: import_mcpBundle.z.boolean().optional().describe("When true, takes a screenshot of the full scrollable page, instead of the currently visible viewport. Cannot be used with element screenshots.")
|
|
55
49
|
});
|
|
56
50
|
const screenshot = (0, import_tool.defineTabTool)({
|
|
57
51
|
capability: "core",
|
|
@@ -63,33 +57,50 @@ const screenshot = (0, import_tool.defineTabTool)({
|
|
|
63
57
|
type: "readOnly"
|
|
64
58
|
},
|
|
65
59
|
handle: async (tab, params, response) => {
|
|
60
|
+
if (!!params.element !== !!params.ref)
|
|
61
|
+
throw new Error("Both element and ref must be provided or neither.");
|
|
62
|
+
if (params.fullPage && params.ref)
|
|
63
|
+
throw new Error("fullPage cannot be used with element screenshots.");
|
|
66
64
|
const fileType = params.type || "png";
|
|
67
|
-
const fileName = await tab.context.outputFile(params.filename ?? `page-${(0, import_utils.dateAsFileName)()}.${fileType}`, { origin: "llm" });
|
|
68
65
|
const options = {
|
|
69
66
|
type: fileType,
|
|
70
67
|
quality: fileType === "png" ? void 0 : 90,
|
|
71
68
|
scale: "css",
|
|
72
|
-
path: fileName,
|
|
73
69
|
...params.fullPage !== void 0 && { fullPage: params.fullPage }
|
|
74
70
|
};
|
|
75
71
|
const isElementScreenshot = params.element && params.ref;
|
|
76
72
|
const screenshotTarget = isElementScreenshot ? params.element : params.fullPage ? "full page" : "viewport";
|
|
73
|
+
const fileName = await response.addFile(params.filename || (0, import_utils3.dateAsFileName)(fileType), { origin: "llm", reason: `Screenshot of ${screenshotTarget}` });
|
|
77
74
|
response.addCode(`// Screenshot ${screenshotTarget} and save it as ${fileName}`);
|
|
78
|
-
const
|
|
79
|
-
if (
|
|
80
|
-
response.addCode(`await page.${
|
|
75
|
+
const ref = params.ref ? await tab.refLocator({ element: params.element || "", ref: params.ref }) : null;
|
|
76
|
+
if (ref)
|
|
77
|
+
response.addCode(`await page.${ref.resolved}.screenshot(${(0, import_utils2.formatObject)(options)});`);
|
|
81
78
|
else
|
|
82
|
-
response.addCode(`await page.screenshot(${
|
|
83
|
-
const buffer =
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
79
|
+
response.addCode(`await page.screenshot(${(0, import_utils2.formatObject)(options)});`);
|
|
80
|
+
const buffer = ref ? await ref.locator.screenshot(options) : await tab.page.screenshot(options);
|
|
81
|
+
await (0, import_utils.mkdirIfNeeded)(fileName);
|
|
82
|
+
await import_fs.default.promises.writeFile(fileName, buffer);
|
|
83
|
+
response.addImage({
|
|
84
|
+
contentType: fileType === "png" ? "image/png" : "image/jpeg",
|
|
85
|
+
data: scaleImageToFitMessage(buffer, fileType)
|
|
86
|
+
});
|
|
91
87
|
}
|
|
92
88
|
});
|
|
89
|
+
function scaleImageToFitMessage(buffer, imageType) {
|
|
90
|
+
const image = imageType === "png" ? import_utilsBundle.PNG.sync.read(buffer) : import_utilsBundle.jpegjs.decode(buffer, { maxMemoryUsageInMB: 512 });
|
|
91
|
+
const pixels = image.width * image.height;
|
|
92
|
+
const shrink = Math.min(1568 / image.width, 1568 / image.height, Math.sqrt(1.15 * 1024 * 1024 / pixels));
|
|
93
|
+
if (shrink > 1)
|
|
94
|
+
return buffer;
|
|
95
|
+
const width = image.width * shrink | 0;
|
|
96
|
+
const height = image.height * shrink | 0;
|
|
97
|
+
const scaledImage = (0, import_utils.scaleImageToSize)(image, { width, height });
|
|
98
|
+
return imageType === "png" ? import_utilsBundle.PNG.sync.write(scaledImage) : import_utilsBundle.jpegjs.encode(scaledImage, 80).data;
|
|
99
|
+
}
|
|
93
100
|
var screenshot_default = [
|
|
94
101
|
screenshot
|
|
95
102
|
];
|
|
103
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
104
|
+
0 && (module.exports = {
|
|
105
|
+
scaleImageToFitMessage
|
|
106
|
+
});
|
|
@@ -32,10 +32,10 @@ __export(snapshot_exports, {
|
|
|
32
32
|
elementSchema: () => elementSchema
|
|
33
33
|
});
|
|
34
34
|
module.exports = __toCommonJS(snapshot_exports);
|
|
35
|
-
var
|
|
35
|
+
var import_fs = __toESM(require("fs"));
|
|
36
|
+
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
|
37
|
+
var import_utils = require("playwright-core/lib/utils");
|
|
36
38
|
var import_tool = require("./tool");
|
|
37
|
-
var javascript = __toESM(require("../codegen"));
|
|
38
|
-
var import_utils = require("./utils");
|
|
39
39
|
var import_common = require("./common");
|
|
40
40
|
const snapshot = (0, import_tool.defineTool)({
|
|
41
41
|
capability: "core",
|
|
@@ -43,22 +43,31 @@ const snapshot = (0, import_tool.defineTool)({
|
|
|
43
43
|
name: "browser_snapshot",
|
|
44
44
|
title: "Page snapshot",
|
|
45
45
|
description: "Capture accessibility snapshot of the current page, this is better than screenshot",
|
|
46
|
-
inputSchema:
|
|
46
|
+
inputSchema: import_mcpBundle.z.object({
|
|
47
|
+
filename: import_mcpBundle.z.string().optional().describe("Save snapshot to markdown file instead of returning it in the response.")
|
|
48
|
+
}),
|
|
47
49
|
type: "readOnly"
|
|
48
50
|
},
|
|
49
51
|
handle: async (context, params, response) => {
|
|
50
52
|
await context.ensureTab();
|
|
51
|
-
response.
|
|
53
|
+
response.setIncludeFullSnapshot();
|
|
54
|
+
if (params.filename) {
|
|
55
|
+
await response.finish();
|
|
56
|
+
const renderedResponse = response.render();
|
|
57
|
+
const fileName = await response.addFile(params.filename, { origin: "llm", reason: "Saved snapshot" });
|
|
58
|
+
await import_fs.default.promises.writeFile(fileName, renderedResponse.asText());
|
|
59
|
+
response.setIncludeMetaOnly();
|
|
60
|
+
}
|
|
52
61
|
}
|
|
53
62
|
});
|
|
54
63
|
const elementSchema = import_common.baseSchema.extend({
|
|
55
|
-
element:
|
|
56
|
-
ref:
|
|
64
|
+
element: import_mcpBundle.z.string().describe("Human-readable element description used to obtain permission to interact with the element"),
|
|
65
|
+
ref: import_mcpBundle.z.string().describe("Exact target element reference from the page snapshot")
|
|
57
66
|
});
|
|
58
67
|
const clickSchema = elementSchema.extend({
|
|
59
|
-
doubleClick:
|
|
60
|
-
button:
|
|
61
|
-
modifiers:
|
|
68
|
+
doubleClick: import_mcpBundle.z.boolean().optional().describe("Whether to perform a double click instead of a single click"),
|
|
69
|
+
button: import_mcpBundle.z.enum(["left", "right", "middle"]).optional().describe("Button to click, defaults to left"),
|
|
70
|
+
modifiers: import_mcpBundle.z.array(import_mcpBundle.z.enum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"])).optional().describe("Modifier keys to press")
|
|
62
71
|
});
|
|
63
72
|
const click = (0, import_tool.defineTabTool)({
|
|
64
73
|
capability: "core",
|
|
@@ -67,21 +76,21 @@ const click = (0, import_tool.defineTabTool)({
|
|
|
67
76
|
title: "Click",
|
|
68
77
|
description: "Perform click on a web page",
|
|
69
78
|
inputSchema: clickSchema,
|
|
70
|
-
type: "
|
|
79
|
+
type: "input"
|
|
71
80
|
},
|
|
72
81
|
handle: async (tab, params, response) => {
|
|
73
82
|
response.setIncludeSnapshot();
|
|
74
|
-
const locator = await tab.refLocator(params);
|
|
83
|
+
const { locator, resolved } = await tab.refLocator(params);
|
|
75
84
|
const options = {
|
|
76
85
|
button: params.button,
|
|
77
86
|
modifiers: params.modifiers
|
|
78
87
|
};
|
|
79
|
-
const formatted =
|
|
88
|
+
const formatted = (0, import_utils.formatObject)(options, " ", "oneline");
|
|
80
89
|
const optionsAttr = formatted !== "{}" ? formatted : "";
|
|
81
90
|
if (params.doubleClick)
|
|
82
|
-
response.addCode(`await page.${
|
|
91
|
+
response.addCode(`await page.${resolved}.dblclick(${optionsAttr});`);
|
|
83
92
|
else
|
|
84
|
-
response.addCode(`await page.${
|
|
93
|
+
response.addCode(`await page.${resolved}.click(${optionsAttr});`);
|
|
85
94
|
await tab.waitForCompletion(async () => {
|
|
86
95
|
if (params.doubleClick)
|
|
87
96
|
await locator.dblclick(options);
|
|
@@ -96,24 +105,24 @@ const drag = (0, import_tool.defineTabTool)({
|
|
|
96
105
|
name: "browser_drag",
|
|
97
106
|
title: "Drag mouse",
|
|
98
107
|
description: "Perform drag and drop between two elements",
|
|
99
|
-
inputSchema:
|
|
100
|
-
startElement:
|
|
101
|
-
startRef:
|
|
102
|
-
endElement:
|
|
103
|
-
endRef:
|
|
108
|
+
inputSchema: import_mcpBundle.z.object({
|
|
109
|
+
startElement: import_mcpBundle.z.string().describe("Human-readable source element description used to obtain the permission to interact with the element"),
|
|
110
|
+
startRef: import_mcpBundle.z.string().describe("Exact source element reference from the page snapshot"),
|
|
111
|
+
endElement: import_mcpBundle.z.string().describe("Human-readable target element description used to obtain the permission to interact with the element"),
|
|
112
|
+
endRef: import_mcpBundle.z.string().describe("Exact target element reference from the page snapshot")
|
|
104
113
|
}),
|
|
105
|
-
type: "
|
|
114
|
+
type: "input"
|
|
106
115
|
},
|
|
107
116
|
handle: async (tab, params, response) => {
|
|
108
117
|
response.setIncludeSnapshot();
|
|
109
|
-
const [
|
|
118
|
+
const [start, end] = await tab.refLocators([
|
|
110
119
|
{ ref: params.startRef, element: params.startElement },
|
|
111
120
|
{ ref: params.endRef, element: params.endElement }
|
|
112
121
|
]);
|
|
113
122
|
await tab.waitForCompletion(async () => {
|
|
114
|
-
await
|
|
123
|
+
await start.locator.dragTo(end.locator);
|
|
115
124
|
});
|
|
116
|
-
response.addCode(`await page.${
|
|
125
|
+
response.addCode(`await page.${start.resolved}.dragTo(page.${end.resolved});`);
|
|
117
126
|
}
|
|
118
127
|
});
|
|
119
128
|
const hover = (0, import_tool.defineTabTool)({
|
|
@@ -123,19 +132,19 @@ const hover = (0, import_tool.defineTabTool)({
|
|
|
123
132
|
title: "Hover mouse",
|
|
124
133
|
description: "Hover over element on page",
|
|
125
134
|
inputSchema: elementSchema,
|
|
126
|
-
type: "
|
|
135
|
+
type: "input"
|
|
127
136
|
},
|
|
128
137
|
handle: async (tab, params, response) => {
|
|
129
138
|
response.setIncludeSnapshot();
|
|
130
|
-
const locator = await tab.refLocator(params);
|
|
131
|
-
response.addCode(`await page.${
|
|
139
|
+
const { locator, resolved } = await tab.refLocator(params);
|
|
140
|
+
response.addCode(`await page.${resolved}.hover();`);
|
|
132
141
|
await tab.waitForCompletion(async () => {
|
|
133
142
|
await locator.hover();
|
|
134
143
|
});
|
|
135
144
|
}
|
|
136
145
|
});
|
|
137
146
|
const selectOptionSchema = elementSchema.extend({
|
|
138
|
-
values:
|
|
147
|
+
values: import_mcpBundle.z.array(import_mcpBundle.z.string()).describe("Array of values to select in the dropdown. This can be a single value or multiple values.")
|
|
139
148
|
});
|
|
140
149
|
const selectOption = (0, import_tool.defineTabTool)({
|
|
141
150
|
capability: "core",
|
|
@@ -144,12 +153,12 @@ const selectOption = (0, import_tool.defineTabTool)({
|
|
|
144
153
|
title: "Select option",
|
|
145
154
|
description: "Select an option in a dropdown",
|
|
146
155
|
inputSchema: selectOptionSchema,
|
|
147
|
-
type: "
|
|
156
|
+
type: "input"
|
|
148
157
|
},
|
|
149
158
|
handle: async (tab, params, response) => {
|
|
150
159
|
response.setIncludeSnapshot();
|
|
151
|
-
const locator = await tab.refLocator(params);
|
|
152
|
-
response.addCode(`await page.${
|
|
160
|
+
const { locator, resolved } = await tab.refLocator(params);
|
|
161
|
+
response.addCode(`await page.${resolved}.selectOption(${(0, import_utils.formatObject)(params.values)});`);
|
|
153
162
|
await tab.waitForCompletion(async () => {
|
|
154
163
|
await locator.selectOption(params.values);
|
|
155
164
|
});
|
|
@@ -165,8 +174,8 @@ const pickLocator = (0, import_tool.defineTabTool)({
|
|
|
165
174
|
type: "readOnly"
|
|
166
175
|
},
|
|
167
176
|
handle: async (tab, params, response) => {
|
|
168
|
-
const
|
|
169
|
-
response.addResult(
|
|
177
|
+
const { resolved } = await tab.refLocator(params);
|
|
178
|
+
response.addResult(resolved);
|
|
170
179
|
}
|
|
171
180
|
});
|
|
172
181
|
var snapshot_default = [
|