feat: add runtime permission granting tools and --grant-all-permissions flag
New tools for managing browser permissions at runtime (no restart needed): - browser_grant_permissions: Grant specific or ALL permissions at runtime - Supports `all: true` to grant all common permissions at once - browser_clear_permissions: Revoke all granted permissions - browser_set_geolocation: Set geolocation coordinates at runtime New CLI flag: - --grant-all-permissions: Start with all permissions pre-granted - PLAYWRIGHT_MCP_GRANT_ALL_PERMISSIONS env var support Permissions granted with `all: true` or --grant-all-permissions: - geolocation, notifications, camera, microphone - clipboard-read, clipboard-write - accelerometer, gyroscope, magnetometer - midi, background-sync, ambient-light-sensor - accessibility-events
This commit is contained in:
parent
5e5123d1ac
commit
0031d17f32
55
README.md
55
README.md
@ -181,6 +181,9 @@ Playwright MCP server supports following arguments. They can be provided in the
|
|||||||
--no-isolated use a persistent browser profile. Enables
|
--no-isolated use a persistent browser profile. Enables
|
||||||
features like Push API that require
|
features like Push API that require
|
||||||
non-incognito mode.
|
non-incognito mode.
|
||||||
|
--grant-all-permissions grant all browser permissions (geolocation,
|
||||||
|
camera, microphone, clipboard, etc.) at
|
||||||
|
startup.
|
||||||
--image-responses <mode> whether to send image responses to the client.
|
--image-responses <mode> whether to send image responses to the client.
|
||||||
Can be "allow" or "omit", Defaults to "allow".
|
Can be "allow" or "omit", Defaults to "allow".
|
||||||
--no-snapshots disable automatic page snapshots after
|
--no-snapshots disable automatic page snapshots after
|
||||||
@ -556,6 +559,14 @@ http.createServer(async (req, res) => {
|
|||||||
|
|
||||||
<!-- NOTE: This has been generated via update-readme.js -->
|
<!-- NOTE: This has been generated via update-readme.js -->
|
||||||
|
|
||||||
|
- **browser_clear_permissions**
|
||||||
|
- Title: Clear all browser permissions
|
||||||
|
- Description: Revoke all previously granted permissions for the current browser context. Sites will need to request permissions again.
|
||||||
|
- Parameters: None
|
||||||
|
- Read-only: **false**
|
||||||
|
|
||||||
|
<!-- NOTE: This has been generated via update-readme.js -->
|
||||||
|
|
||||||
- **browser_clear_requests**
|
- **browser_clear_requests**
|
||||||
- Title: Clear captured requests
|
- Title: Clear captured requests
|
||||||
- Description: Clear all captured HTTP request data from memory. Useful for freeing up memory during long sessions or when starting fresh analysis.
|
- Description: Clear all captured HTTP request data from memory. Useful for freeing up memory during long sessions or when starting fresh analysis.
|
||||||
@ -835,6 +846,39 @@ This is the FIRST conversational browser automation MCP server!
|
|||||||
|
|
||||||
<!-- NOTE: This has been generated via update-readme.js -->
|
<!-- NOTE: This has been generated via update-readme.js -->
|
||||||
|
|
||||||
|
- **browser_grant_permissions**
|
||||||
|
- Title: Grant browser permissions at runtime
|
||||||
|
- Description: Grant browser permissions at runtime without restarting the browser. This is faster than using browser_configure which requires a browser restart.
|
||||||
|
|
||||||
|
**Quick option:** Use `all: true` to grant all common permissions at once!
|
||||||
|
|
||||||
|
**Available permissions:**
|
||||||
|
- geolocation - Access user location
|
||||||
|
- notifications - Show browser notifications
|
||||||
|
- camera - Access camera/webcam
|
||||||
|
- microphone - Access microphone
|
||||||
|
- clipboard-read - Read from clipboard
|
||||||
|
- clipboard-write - Write to clipboard
|
||||||
|
- accelerometer - Access motion sensors
|
||||||
|
- gyroscope - Access orientation sensors
|
||||||
|
- magnetometer - Access compass
|
||||||
|
- accessibility-events - Accessibility automation
|
||||||
|
- midi - MIDI device access
|
||||||
|
- midi-sysex - MIDI system exclusive messages
|
||||||
|
- background-sync - Background sync API
|
||||||
|
- ambient-light-sensor - Light sensor access
|
||||||
|
- payment-handler - Payment request API
|
||||||
|
- storage-access - Storage access API
|
||||||
|
|
||||||
|
**Note:** Some permissions may require user interaction (like camera/microphone device selection) even after being granted.
|
||||||
|
- Parameters:
|
||||||
|
- `permissions` (array, optional): List of permissions to grant (e.g., ["geolocation", "camera", "microphone"])
|
||||||
|
- `all` (boolean, optional): Grant ALL common permissions at once (geolocation, notifications, camera, microphone, clipboard, sensors, midi)
|
||||||
|
- `origin` (string, optional): Origin to grant permissions for (e.g., "https://example.com"). If not specified, grants for all origins.
|
||||||
|
- Read-only: **false**
|
||||||
|
|
||||||
|
<!-- NOTE: This has been generated via update-readme.js -->
|
||||||
|
|
||||||
- **browser_handle_dialog**
|
- **browser_handle_dialog**
|
||||||
- Title: Handle a dialog
|
- Title: Handle a dialog
|
||||||
- Description: Handle a dialog. Returns page snapshot after handling dialog (configurable via browser_configure_snapshots).
|
- Description: Handle a dialog. Returns page snapshot after handling dialog (configurable via browser_configure_snapshots).
|
||||||
@ -1101,6 +1145,17 @@ Full API: See MODEL-COLLABORATION-API.md
|
|||||||
|
|
||||||
<!-- NOTE: This has been generated via update-readme.js -->
|
<!-- NOTE: This has been generated via update-readme.js -->
|
||||||
|
|
||||||
|
- **browser_set_geolocation**
|
||||||
|
- Title: Set geolocation at runtime
|
||||||
|
- Description: Set the browser's geolocation at runtime without restarting. Automatically grants geolocation permission.
|
||||||
|
- Parameters:
|
||||||
|
- `latitude` (number): Latitude coordinate (-90 to 90)
|
||||||
|
- `longitude` (number): Longitude coordinate (-180 to 180)
|
||||||
|
- `accuracy` (number, optional): Accuracy in meters (default: 100)
|
||||||
|
- Read-only: **false**
|
||||||
|
|
||||||
|
<!-- NOTE: This has been generated via update-readme.js -->
|
||||||
|
|
||||||
- **browser_set_offline**
|
- **browser_set_offline**
|
||||||
- Title: Set browser offline mode
|
- Title: Set browser offline mode
|
||||||
- Description: Toggle browser offline mode on/off (equivalent to DevTools offline checkbox)
|
- Description: Toggle browser offline mode on/off (equivalent to DevTools offline checkbox)
|
||||||
|
|||||||
@ -34,6 +34,7 @@ export type CLIOptions = {
|
|||||||
consoleOutputFile?: string;
|
consoleOutputFile?: string;
|
||||||
device?: string;
|
device?: string;
|
||||||
executablePath?: string;
|
executablePath?: string;
|
||||||
|
grantAllPermissions?: boolean;
|
||||||
headless?: boolean;
|
headless?: boolean;
|
||||||
host?: string;
|
host?: string;
|
||||||
ignoreHttpsErrors?: boolean;
|
ignoreHttpsErrors?: boolean;
|
||||||
@ -191,6 +192,25 @@ export function configFromCLIOptions(cliOptions: CLIOptions): Config {
|
|||||||
if (cliOptions.blockServiceWorkers)
|
if (cliOptions.blockServiceWorkers)
|
||||||
contextOptions.serviceWorkers = 'block';
|
contextOptions.serviceWorkers = 'block';
|
||||||
|
|
||||||
|
// Grant all permissions if requested
|
||||||
|
if (cliOptions.grantAllPermissions) {
|
||||||
|
contextOptions.permissions = [
|
||||||
|
'geolocation',
|
||||||
|
'notifications',
|
||||||
|
'camera',
|
||||||
|
'microphone',
|
||||||
|
'clipboard-read',
|
||||||
|
'clipboard-write',
|
||||||
|
'accelerometer',
|
||||||
|
'gyroscope',
|
||||||
|
'magnetometer',
|
||||||
|
'midi',
|
||||||
|
'background-sync',
|
||||||
|
'ambient-light-sensor',
|
||||||
|
'accessibility-events',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
const result: Config = {
|
const result: Config = {
|
||||||
browser: {
|
browser: {
|
||||||
browserName,
|
browserName,
|
||||||
@ -236,6 +256,7 @@ function configFromEnv(): Config {
|
|||||||
options.config = envToString(process.env.PLAYWRIGHT_MCP_CONFIG);
|
options.config = envToString(process.env.PLAYWRIGHT_MCP_CONFIG);
|
||||||
options.device = envToString(process.env.PLAYWRIGHT_MCP_DEVICE);
|
options.device = envToString(process.env.PLAYWRIGHT_MCP_DEVICE);
|
||||||
options.executablePath = envToString(process.env.PLAYWRIGHT_MCP_EXECUTABLE_PATH);
|
options.executablePath = envToString(process.env.PLAYWRIGHT_MCP_EXECUTABLE_PATH);
|
||||||
|
options.grantAllPermissions = envToBoolean(process.env.PLAYWRIGHT_MCP_GRANT_ALL_PERMISSIONS);
|
||||||
options.headless = envToBoolean(process.env.PLAYWRIGHT_MCP_HEADLESS);
|
options.headless = envToBoolean(process.env.PLAYWRIGHT_MCP_HEADLESS);
|
||||||
options.host = envToString(process.env.PLAYWRIGHT_MCP_HOST);
|
options.host = envToString(process.env.PLAYWRIGHT_MCP_HOST);
|
||||||
options.ignoreHttpsErrors = envToBoolean(process.env.PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS);
|
options.ignoreHttpsErrors = envToBoolean(process.env.PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS);
|
||||||
|
|||||||
@ -46,6 +46,7 @@ program
|
|||||||
.option('--ignore-https-errors', 'ignore https errors')
|
.option('--ignore-https-errors', 'ignore https errors')
|
||||||
.option('--isolated', 'keep the browser profile in memory, do not save it to disk. This is the default.')
|
.option('--isolated', 'keep the browser profile in memory, do not save it to disk. This is the default.')
|
||||||
.option('--no-isolated', 'use a persistent browser profile. Enables features like Push API that require non-incognito mode.')
|
.option('--no-isolated', 'use a persistent browser profile. Enables features like Push API that require non-incognito mode.')
|
||||||
|
.option('--grant-all-permissions', 'grant all browser permissions (geolocation, camera, microphone, clipboard, etc.) at startup.')
|
||||||
.option('--image-responses <mode>', 'whether to send image responses to the client. Can be "allow" or "omit", Defaults to "allow".')
|
.option('--image-responses <mode>', 'whether to send image responses to the client. Can be "allow" or "omit", Defaults to "allow".')
|
||||||
.option('--no-snapshots', 'disable automatic page snapshots after interactive operations like clicks. Use browser_snapshot tool for explicit snapshots.')
|
.option('--no-snapshots', 'disable automatic page snapshots after interactive operations like clicks. Use browser_snapshot tool for explicit snapshots.')
|
||||||
.option('--max-snapshot-tokens <tokens>', 'maximum number of tokens allowed in page snapshots before truncation. Use 0 to disable truncation. Default is 10000.', parseInt)
|
.option('--max-snapshot-tokens <tokens>', 'maximum number of tokens allowed in page snapshots before truncation. Use 0 to disable truncation. Default is 10000.', parseInt)
|
||||||
|
|||||||
@ -235,10 +235,158 @@ const clearNotifications = defineTool({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// All commonly-used permissions that can be granted
|
||||||
|
const ALL_PERMISSIONS = [
|
||||||
|
'geolocation',
|
||||||
|
'notifications',
|
||||||
|
'camera',
|
||||||
|
'microphone',
|
||||||
|
'clipboard-read',
|
||||||
|
'clipboard-write',
|
||||||
|
'accelerometer',
|
||||||
|
'gyroscope',
|
||||||
|
'magnetometer',
|
||||||
|
'midi',
|
||||||
|
'background-sync',
|
||||||
|
'ambient-light-sensor',
|
||||||
|
'accessibility-events',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grant permissions at runtime without restarting the browser.
|
||||||
|
* More flexible than browser_configure which requires a restart.
|
||||||
|
*/
|
||||||
|
const grantPermissions = defineTool({
|
||||||
|
capability: 'core',
|
||||||
|
|
||||||
|
schema: {
|
||||||
|
name: 'browser_grant_permissions',
|
||||||
|
title: 'Grant browser permissions at runtime',
|
||||||
|
description: `Grant browser permissions at runtime without restarting the browser. This is faster than using browser_configure which requires a browser restart.
|
||||||
|
|
||||||
|
**Quick option:** Use \`all: true\` to grant all common permissions at once!
|
||||||
|
|
||||||
|
**Available permissions:**
|
||||||
|
- geolocation - Access user location
|
||||||
|
- notifications - Show browser notifications
|
||||||
|
- camera - Access camera/webcam
|
||||||
|
- microphone - Access microphone
|
||||||
|
- clipboard-read - Read from clipboard
|
||||||
|
- clipboard-write - Write to clipboard
|
||||||
|
- accelerometer - Access motion sensors
|
||||||
|
- gyroscope - Access orientation sensors
|
||||||
|
- magnetometer - Access compass
|
||||||
|
- accessibility-events - Accessibility automation
|
||||||
|
- midi - MIDI device access
|
||||||
|
- midi-sysex - MIDI system exclusive messages
|
||||||
|
- background-sync - Background sync API
|
||||||
|
- ambient-light-sensor - Light sensor access
|
||||||
|
- payment-handler - Payment request API
|
||||||
|
- storage-access - Storage access API
|
||||||
|
|
||||||
|
**Note:** Some permissions may require user interaction (like camera/microphone device selection) even after being granted.`,
|
||||||
|
inputSchema: z.object({
|
||||||
|
permissions: z.array(z.string()).optional().describe('List of permissions to grant (e.g., ["geolocation", "camera", "microphone"])'),
|
||||||
|
all: z.boolean().optional().describe('Grant ALL common permissions at once (geolocation, notifications, camera, microphone, clipboard, sensors, midi)'),
|
||||||
|
origin: z.string().optional().describe('Origin to grant permissions for (e.g., "https://example.com"). If not specified, grants for all origins.'),
|
||||||
|
}),
|
||||||
|
type: 'destructive',
|
||||||
|
},
|
||||||
|
|
||||||
|
handle: async (context, params, response) => {
|
||||||
|
const browserContext = await context.existingBrowserContext();
|
||||||
|
if (!browserContext)
|
||||||
|
throw new Error('No browser context available. Navigate to a page first.');
|
||||||
|
|
||||||
|
// Determine which permissions to grant
|
||||||
|
let permissionsToGrant: string[];
|
||||||
|
if (params.all) {
|
||||||
|
permissionsToGrant = ALL_PERMISSIONS;
|
||||||
|
} else if (params.permissions && params.permissions.length > 0) {
|
||||||
|
permissionsToGrant = params.permissions;
|
||||||
|
} else {
|
||||||
|
throw new Error('Either specify "permissions" array or set "all: true" to grant all permissions.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const grantOptions = params.origin ? { origin: params.origin } : undefined;
|
||||||
|
|
||||||
|
await browserContext.grantPermissions(permissionsToGrant, grantOptions);
|
||||||
|
|
||||||
|
const scope = params.origin ? `for ${params.origin}` : 'for all origins';
|
||||||
|
const header = params.all ? '✅ Granted ALL permissions' : '✅ Granted permissions';
|
||||||
|
response.addResult(`${header} ${scope}:\n${permissionsToGrant.map(p => ` • ${p}`).join('\n')}`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all granted permissions.
|
||||||
|
*/
|
||||||
|
const clearPermissions = defineTool({
|
||||||
|
capability: 'core',
|
||||||
|
|
||||||
|
schema: {
|
||||||
|
name: 'browser_clear_permissions',
|
||||||
|
title: 'Clear all browser permissions',
|
||||||
|
description: 'Revoke all previously granted permissions for the current browser context. Sites will need to request permissions again.',
|
||||||
|
inputSchema: z.object({}),
|
||||||
|
type: 'destructive',
|
||||||
|
},
|
||||||
|
|
||||||
|
handle: async (context, _params, response) => {
|
||||||
|
const browserContext = await context.existingBrowserContext();
|
||||||
|
if (!browserContext)
|
||||||
|
throw new Error('No browser context available. Navigate to a page first.');
|
||||||
|
|
||||||
|
await browserContext.clearPermissions();
|
||||||
|
|
||||||
|
response.addResult('✅ All permissions have been cleared. Sites will need to request permissions again.');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set geolocation at runtime.
|
||||||
|
*/
|
||||||
|
const setGeolocation = defineTool({
|
||||||
|
capability: 'core',
|
||||||
|
|
||||||
|
schema: {
|
||||||
|
name: 'browser_set_geolocation',
|
||||||
|
title: 'Set geolocation at runtime',
|
||||||
|
description: 'Set the browser\'s geolocation at runtime without restarting. Automatically grants geolocation permission.',
|
||||||
|
inputSchema: z.object({
|
||||||
|
latitude: z.number().min(-90).max(90).describe('Latitude coordinate (-90 to 90)'),
|
||||||
|
longitude: z.number().min(-180).max(180).describe('Longitude coordinate (-180 to 180)'),
|
||||||
|
accuracy: z.number().optional().describe('Accuracy in meters (default: 100)'),
|
||||||
|
}),
|
||||||
|
type: 'destructive',
|
||||||
|
},
|
||||||
|
|
||||||
|
handle: async (context, params, response) => {
|
||||||
|
const browserContext = await context.existingBrowserContext();
|
||||||
|
if (!browserContext)
|
||||||
|
throw new Error('No browser context available. Navigate to a page first.');
|
||||||
|
|
||||||
|
// Grant geolocation permission first
|
||||||
|
await browserContext.grantPermissions(['geolocation']);
|
||||||
|
|
||||||
|
// Set the geolocation
|
||||||
|
await browserContext.setGeolocation({
|
||||||
|
latitude: params.latitude,
|
||||||
|
longitude: params.longitude,
|
||||||
|
accuracy: params.accuracy || 100,
|
||||||
|
});
|
||||||
|
|
||||||
|
response.addResult(`✅ Geolocation set to:\n • Latitude: ${params.latitude}\n • Longitude: ${params.longitude}\n • Accuracy: ${params.accuracy || 100}m\n\nGeolocation permission has been automatically granted.`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
configureNotifications,
|
configureNotifications,
|
||||||
listNotifications,
|
listNotifications,
|
||||||
handleNotification,
|
handleNotification,
|
||||||
waitForNotification,
|
waitForNotification,
|
||||||
clearNotifications,
|
clearNotifications,
|
||||||
|
grantPermissions,
|
||||||
|
clearPermissions,
|
||||||
|
setGeolocation,
|
||||||
];
|
];
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user