chore: introduce browser_evaluate (#678)

Fixes https://github.com/microsoft/playwright-mcp/issues/424
This commit is contained in:
Pavel Feldman
2025-07-16 15:02:47 -07:00
committed by GitHub
parent 825a97d66e
commit 012c906500
9 changed files with 127 additions and 36 deletions

View File

@@ -190,19 +190,19 @@ ${code.join('\n')}
result.push('');
}
if (this.tabs().length > 1)
result.push(await this.listTabsMarkdown(), '');
if (captureSnapshot && tab.hasSnapshot()) {
if (this.tabs().length > 1)
result.push(await this.listTabsMarkdown(), '');
if (this.tabs().length > 1)
result.push('### Current tab');
if (this.tabs().length > 1)
result.push('### Current tab');
result.push(
`- Page URL: ${tab.page.url()}`,
`- Page Title: ${await tab.title()}`
);
if (captureSnapshot && tab.hasSnapshot())
result.push(
`- Page URL: ${tab.page.url()}`,
`- Page Title: ${await tab.title()}`
);
result.push(tab.snapshotOrDie().text());
}
const content = actionResult?.content ?? [];

View File

@@ -17,6 +17,7 @@
import common from './tools/common.js';
import console from './tools/console.js';
import dialogs from './tools/dialogs.js';
import evaluate from './tools/evaluate.js';
import files from './tools/files.js';
import install from './tools/install.js';
import keyboard from './tools/keyboard.js';
@@ -35,6 +36,7 @@ export const snapshotTools: Tool<any>[] = [
...common(true),
...console,
...dialogs(true),
...evaluate,
...files(true),
...install,
...keyboard(true),

71
src/tools/evaluate.ts Normal file
View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { z } from 'zod';
import { defineTool } from './tool.js';
import * as javascript from '../javascript.js';
import { generateLocator } from './utils.js';
import type * as playwright from 'playwright';
const evaluateSchema = z.object({
function: z.string().describe('() => { /* code */ } or (element) => { /* code */ } when element is provided'),
element: z.string().optional().describe('Human-readable element description used to obtain permission to interact with the element'),
ref: z.string().optional().describe('Exact target element reference from the page snapshot'),
});
const evaluate = defineTool({
capability: 'core',
schema: {
name: 'browser_evaluate',
title: 'Evaluate JavaScript',
description: 'Evaluate JavaScript expression on page or element',
inputSchema: evaluateSchema,
type: 'destructive',
},
handle: async (context, params) => {
const tab = context.currentTabOrDie();
const code: string[] = [];
let locator: playwright.Locator | undefined;
if (params.ref && params.element) {
const snapshot = tab.snapshotOrDie();
locator = snapshot.refLocator({ ref: params.ref, element: params.element });
code.push(`await page.${await generateLocator(locator)}.evaluate(${javascript.quote(params.function)});`);
} else {
code.push(`await page.evaluate(${javascript.quote(params.function)});`);
}
return {
code,
action: async () => {
const receiver = locator ?? tab.page as any;
const result = await receiver._evaluateFunction(params.function);
return {
content: [{ type: 'text', text: '- Result: ' + (JSON.stringify(result, null, 2) || 'undefined') }],
};
},
captureSnapshot: false,
waitForNetwork: false,
};
},
});
export default [
evaluate,
];