chore: introduce capabilities argument (#135)

This commit is contained in:
Pavel Feldman
2025-04-04 17:14:30 -07:00
committed by GitHub
parent 707ebbf4d4
commit abd56f514b
24 changed files with 432 additions and 242 deletions

View File

@@ -282,7 +282,7 @@ class PageSnapshot {
results.push('');
}
if (options?.hasFileChooser) {
results.push('- There is a file chooser visible that requires browser_choose_file to be called');
results.push('- There is a file chooser visible that requires browser_file_upload to be called');
results.push('');
}
results.push(this._text);

View File

@@ -16,7 +16,7 @@
import { createServerWithTools } from './server';
import common from './tools/common';
import fileChooser from './tools/fileChooser';
import files from './tools/files';
import install from './tools/install';
import keyboard from './tools/keyboard';
import navigate from './tools/navigate';
@@ -24,16 +24,16 @@ import pdf from './tools/pdf';
import snapshot from './tools/snapshot';
import tabs from './tools/tabs';
import screen from './tools/screen';
import { console } from './resources/console';
import { console as consoleResource } from './resources/console';
import type { Tool } from './tools/tool';
import type { Tool, ToolCapability } from './tools/tool';
import type { Resource } from './resources/resource';
import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
import type { LaunchOptions } from 'playwright';
const snapshotTools: Tool[] = [
...common,
...fileChooser(true),
...files(true),
...install,
...keyboard(true),
...navigate(true),
@@ -44,7 +44,7 @@ const snapshotTools: Tool[] = [
const screenshotTools: Tool[] = [
...common,
...fileChooser(false),
...files(false),
...install,
...keyboard(false),
...navigate(false),
@@ -54,7 +54,7 @@ const screenshotTools: Tool[] = [
];
const resources: Resource[] = [
console,
consoleResource,
];
type Options = {
@@ -63,12 +63,14 @@ type Options = {
launchOptions?: LaunchOptions;
cdpEndpoint?: string;
vision?: boolean;
capabilities?: ToolCapability[];
};
const packageJSON = require('../package.json');
export function createServer(options?: Options): Server {
const tools = options?.vision ? screenshotTools : snapshotTools;
const allTools = options?.vision ? screenshotTools : snapshotTools;
const tools = allTools.filter(tool => !options?.capabilities || tool.capability === 'core' || options.capabilities.includes(tool.capability));
return createServerWithTools({
name: 'Playwright',
version: packageJSON.version,

View File

@@ -29,6 +29,7 @@ import { ServerList } from './server';
import type { LaunchOptions } from 'playwright';
import assert from 'assert';
import { ToolCapability } from './tools/tool';
const packageJSON = require('../package.json');
@@ -36,6 +37,7 @@ program
.version('Version ' + packageJSON.version)
.name(packageJSON.name)
.option('--browser <browser>', 'Browser or chrome channel to use, possible values: chrome, firefox, webkit, msedge.')
.option('--caps <caps>', 'Comma-separated list of capabilities to enable, possible values: tabs, pdf, history, wait, files, install. Default is all.')
.option('--cdp-endpoint <endpoint>', 'CDP endpoint to connect to.')
.option('--executable-path <path>', 'Path to the browser executable.')
.option('--headless', 'Run browser in headless mode, headed by default')
@@ -85,6 +87,7 @@ program
launchOptions,
vision: !!options.vision,
cdpEndpoint: options.cdpEndpoint,
capabilities: options.caps?.split(',').map((c: string) => c.trim() as ToolCapability),
}));
setupExitWatchdog(serverList);

View File

@@ -24,6 +24,7 @@ const waitSchema = z.object({
});
const wait: Tool = {
capability: 'wait',
schema: {
name: 'browser_wait',
description: 'Wait for a specified time in seconds',
@@ -44,6 +45,7 @@ const wait: Tool = {
const closeSchema = z.object({});
const close: Tool = {
capability: 'core',
schema: {
name: 'browser_close',
description: 'Close the page',

View File

@@ -19,18 +19,19 @@ import { zodToJsonSchema } from 'zod-to-json-schema';
import type { ToolFactory } from './tool';
const chooseFileSchema = z.object({
const uploadFileSchema = z.object({
paths: z.array(z.string()).describe('The absolute paths to the files to upload. Can be a single file or multiple files.'),
});
const chooseFile: ToolFactory = captureSnapshot => ({
const uploadFile: ToolFactory = captureSnapshot => ({
capability: 'files',
schema: {
name: 'browser_choose_file',
description: 'Choose one or multiple files to upload',
inputSchema: zodToJsonSchema(chooseFileSchema),
name: 'browser_file_upload',
description: 'Upload one or multiple files',
inputSchema: zodToJsonSchema(uploadFileSchema),
},
handle: async (context, params) => {
const validatedParams = chooseFileSchema.parse(params);
const validatedParams = uploadFileSchema.parse(params);
const tab = context.currentTab();
return await tab.runAndWait(async () => {
await tab.submitFileChooser(validatedParams.paths);
@@ -43,5 +44,5 @@ const chooseFile: ToolFactory = captureSnapshot => ({
});
export default (captureSnapshot: boolean) => [
chooseFile(captureSnapshot),
uploadFile(captureSnapshot),
];

View File

@@ -23,6 +23,7 @@ import { zodToJsonSchema } from 'zod-to-json-schema';
import type { Tool } from './tool';
const install: Tool = {
capability: 'install',
schema: {
name: 'browser_install',
description: 'Install the browser specified in the config. Call this if you get an error about the browser not being installed.',

View File

@@ -24,6 +24,7 @@ const pressKeySchema = z.object({
});
const pressKey: ToolFactory = captureSnapshot => ({
capability: 'core',
schema: {
name: 'browser_press_key',
description: 'Press a key on the keyboard',

View File

@@ -24,6 +24,7 @@ const navigateSchema = z.object({
});
const navigate: ToolFactory = captureSnapshot => ({
capability: 'core',
schema: {
name: 'browser_navigate',
description: 'Navigate to a URL',
@@ -44,6 +45,7 @@ const navigate: ToolFactory = captureSnapshot => ({
const goBackSchema = z.object({});
const goBack: ToolFactory = snapshot => ({
capability: 'history',
schema: {
name: 'browser_navigate_back',
description: 'Go back to the previous page',
@@ -62,6 +64,7 @@ const goBack: ToolFactory = snapshot => ({
const goForwardSchema = z.object({});
const goForward: ToolFactory = snapshot => ({
capability: 'history',
schema: {
name: 'browser_navigate_forward',
description: 'Go forward to the next page',

View File

@@ -27,6 +27,7 @@ import type { Tool } from './tool';
const pdfSchema = z.object({});
const pdf: Tool = {
capability: 'pdf',
schema: {
name: 'browser_pdf_save',
description: 'Save page as PDF',

View File

@@ -20,6 +20,7 @@ import { zodToJsonSchema } from 'zod-to-json-schema';
import type { Tool } from './tool';
const screenshot: Tool = {
capability: 'core',
schema: {
name: 'browser_screen_capture',
description: 'Take a screenshot of the current page',
@@ -45,6 +46,7 @@ const moveMouseSchema = elementSchema.extend({
});
const moveMouse: Tool = {
capability: 'core',
schema: {
name: 'browser_screen_move_mouse',
description: 'Move mouse to a given position',
@@ -67,6 +69,7 @@ const clickSchema = elementSchema.extend({
});
const click: Tool = {
capability: 'core',
schema: {
name: 'browser_screen_click',
description: 'Click left mouse button',
@@ -93,6 +96,7 @@ const dragSchema = elementSchema.extend({
});
const drag: Tool = {
capability: 'core',
schema: {
name: 'browser_screen_drag',
description: 'Drag left mouse button',
@@ -118,6 +122,7 @@ const typeSchema = z.object({
});
const type: Tool = {
capability: 'core',
schema: {
name: 'browser_screen_type',
description: 'Type text',

View File

@@ -21,6 +21,7 @@ import type * as playwright from 'playwright';
import type { Tool } from './tool';
const snapshot: Tool = {
capability: 'core',
schema: {
name: 'browser_snapshot',
description: 'Capture accessibility snapshot of the current page, this is better than screenshot',
@@ -38,6 +39,7 @@ const elementSchema = z.object({
});
const click: Tool = {
capability: 'core',
schema: {
name: 'browser_click',
description: 'Perform click on a web page',
@@ -63,6 +65,7 @@ const dragSchema = z.object({
});
const drag: Tool = {
capability: 'core',
schema: {
name: 'browser_drag',
description: 'Perform drag and drop between two elements',
@@ -82,6 +85,7 @@ const drag: Tool = {
};
const hover: Tool = {
capability: 'core',
schema: {
name: 'browser_hover',
description: 'Hover over element on page',
@@ -106,6 +110,7 @@ const typeSchema = elementSchema.extend({
});
const type: Tool = {
capability: 'core',
schema: {
name: 'browser_type',
description: 'Type text into editable element',
@@ -133,6 +138,7 @@ const selectOptionSchema = elementSchema.extend({
});
const selectOption: Tool = {
capability: 'core',
schema: {
name: 'browser_select_option',
description: 'Select an option in a dropdown',
@@ -155,6 +161,7 @@ const screenshotSchema = z.object({
});
const screenshot: Tool = {
capability: 'core',
schema: {
name: 'browser_take_screenshot',
description: `Take a screenshot of the current page. You can't perform actions based on the screenshot, use browser_snapshot for actions.`,

View File

@@ -20,6 +20,7 @@ import { zodToJsonSchema } from 'zod-to-json-schema';
import type { ToolFactory, Tool } from './tool';
const listTabs: Tool = {
capability: 'tabs',
schema: {
name: 'browser_tab_list',
description: 'List browser tabs',
@@ -40,6 +41,7 @@ const selectTabSchema = z.object({
});
const selectTab: ToolFactory = captureSnapshot => ({
capability: 'tabs',
schema: {
name: 'browser_tab_select',
description: 'Select a tab by index',
@@ -58,6 +60,7 @@ const newTabSchema = z.object({
});
const newTab: Tool = {
capability: 'tabs',
schema: {
name: 'browser_tab_new',
description: 'Open a new tab',
@@ -77,6 +80,7 @@ const closeTabSchema = z.object({
});
const closeTab: ToolFactory = captureSnapshot => ({
capability: 'tabs',
schema: {
name: 'browser_tab_close',
description: 'Close a tab',

View File

@@ -18,6 +18,8 @@ import type { ImageContent, TextContent } from '@modelcontextprotocol/sdk/types'
import type { JsonSchema7Type } from 'zod-to-json-schema';
import type { Context } from '../context';
export type ToolCapability = 'core' | 'tabs' | 'pdf' | 'history' | 'wait' | 'files' | 'install';
export type ToolSchema = {
name: string;
description: string;
@@ -30,6 +32,7 @@ export type ToolResult = {
};
export type Tool = {
capability: ToolCapability;
schema: ToolSchema;
handle: (context: Context, params?: Record<string, any>) => Promise<ToolResult>;
};