-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Expand file tree
/
Copy pathscript.ts
More file actions
86 lines (83 loc) · 2.64 KB
/
script.ts
File metadata and controls
86 lines (83 loc) · 2.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import type {Frame, JSHandle, Page} from 'puppeteer-core';
import {zod} from '../third_party/modelcontextprotocol-sdk/index.js';
import {ToolCategories} from './categories.js';
import {defineTool} from './ToolDefinition.js';
export const evaluateScript = defineTool({
name: 'evaluate_script',
description: `Evaluate a JavaScript function inside the currently selected page. Returns the response as JSON
so returned values have to JSON-serializable.`,
annotations: {
category: ToolCategories.DEBUGGING,
readOnlyHint: false,
},
schema: {
function: zod.string().describe(
`A JavaScript function declaration to be executed by the tool in the currently selected page.
Example without arguments: \`() => {
return document.title
}\` or \`async () => {
return await fetch("example.com")
}\`.
Example with arguments: \`(el) => {
return el.innerText;
}\`
`,
),
args: zod
.array(
zod.object({
uid: zod
.string()
.describe(
'The uid of an element on the page from the page content snapshot',
),
}),
)
.optional()
.describe(`An optional list of arguments to pass to the function.`),
},
handler: async (request, response, context) => {
const args: Array<JSHandle<unknown>> = [];
try {
const frames = new Set<Frame>();
for (const el of request.params.args ?? []) {
const handle = await context.getElementByUid(el.uid);
frames.add(handle.frame);
args.push(handle);
}
let pageOrFrame: Page | Frame;
// We can't evaluate the element handle across frames
if (frames.size > 1) {
throw new Error(
"Elements from different frames can't be evaluated together.",
);
} else {
pageOrFrame = [...frames.values()][0] ?? context.getSelectedPage();
}
const fn = await pageOrFrame.evaluateHandle(
`(${request.params.function})`,
);
args.unshift(fn);
await context.waitForEventsAfterAction(async () => {
const result = await pageOrFrame.evaluate(
async (fn, ...args) => {
// @ts-expect-error no types.
return JSON.stringify(await fn(...args));
},
...args,
);
response.appendResponseLine('Script ran on page and returned:');
response.appendResponseLine('```json');
response.appendResponseLine(`${result}`);
response.appendResponseLine('```');
});
} finally {
void Promise.allSettled(args.map(arg => arg.dispose()));
}
},
});