Skip to content

Commit f07c8c0

Browse files
authored
chore(mcp): --grant-permissions cli option (#37431)
1 parent c3da3d0 commit f07c8c0

File tree

3 files changed

+54
-0
lines changed

3 files changed

+54
-0
lines changed

packages/playwright/src/mcp/browser/config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export type CLIOptions = {
3434
config?: string;
3535
device?: string;
3636
executablePath?: string;
37+
grantPermissions?: string[];
3738
headless?: boolean;
3839
host?: string;
3940
ignoreHttpsErrors?: boolean;
@@ -182,6 +183,9 @@ export function configFromCLIOptions(cliOptions: CLIOptions): Config {
182183
if (cliOptions.blockServiceWorkers)
183184
contextOptions.serviceWorkers = 'block';
184185

186+
if (cliOptions.grantPermissions)
187+
contextOptions.permissions = cliOptions.grantPermissions;
188+
185189
const result: Config = {
186190
browser: {
187191
browserName,
@@ -227,6 +231,7 @@ function configFromEnv(): Config {
227231
options.config = envToString(process.env.PLAYWRIGHT_MCP_CONFIG);
228232
options.device = envToString(process.env.PLAYWRIGHT_MCP_DEVICE);
229233
options.executablePath = envToString(process.env.PLAYWRIGHT_MCP_EXECUTABLE_PATH);
234+
options.grantPermissions = commaSeparatedList(process.env.PLAYWRIGHT_MCP_GRANT_PERMISSIONS);
230235
options.headless = envToBoolean(process.env.PLAYWRIGHT_MCP_HEADLESS);
231236
options.host = envToString(process.env.PLAYWRIGHT_MCP_HOST);
232237
options.ignoreHttpsErrors = envToBoolean(process.env.PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS);

packages/playwright/src/mcp/program.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export function decorateCommand(command: Command, version: string) {
3939
.option('--device <device>', 'device to emulate, for example: "iPhone 15"')
4040
.option('--executable-path <path>', 'path to the browser executable.')
4141
.option('--extension', 'Connect to a running browser instance (Edge/Chrome only). Requires the "Playwright MCP Bridge" browser extension to be installed.')
42+
.option('--grant-permissions <permissions...>', 'List of permissions to grant to the browser context, for example "geolocation", "clipboard-read", "clipboard-write".', commaSeparatedList)
4243
.option('--headless', 'run browser in headless mode, headed by default')
4344
.option('--host <host>', 'host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.')
4445
.option('--ignore-https-errors', 'ignore https errors')

tests/mcp/clipboard.spec.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* Copyright (c) Microsoft Corporation.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { test, expect } from './fixtures';
18+
19+
test('clipboard write without permission dialog', async ({ startClient, server, mcpBrowser }) => {
20+
test.skip(mcpBrowser === 'firefox' || mcpBrowser === 'webkit', 'Clipboard permissions are fully supported only in Chromium');
21+
const { client } = await startClient({
22+
args: [`--grant-permissions=clipboard-read,clipboard-write`]
23+
});
24+
await client.callTool({
25+
name: 'browser_navigate',
26+
arguments: { url: server.HELLO_WORLD },
27+
});
28+
const writeResult = await client.callTool({
29+
name: 'browser_evaluate',
30+
arguments: {
31+
function: `() => navigator.clipboard.writeText('Hello from Playwright!').then(
32+
() => 'Write successful',
33+
e => 'Write failed: ' + e.message)`,
34+
},
35+
});
36+
expect(writeResult).toHaveResponse({
37+
result: '"Write successful"',
38+
});
39+
const readResult = await client.callTool({
40+
name: 'browser_evaluate',
41+
arguments: {
42+
function: `() => navigator.clipboard.readText()`,
43+
},
44+
});
45+
expect(readResult).toHaveResponse({
46+
result: '"Hello from Playwright!"',
47+
});
48+
});

0 commit comments

Comments
 (0)