Address review feedback: move permissions to contextOptions, remove redundant tests and files
Co-authored-by: pavelfeldman <883973+pavelfeldman@users.noreply.github.com>
This commit is contained in:
5
config.d.ts
vendored
5
config.d.ts
vendored
@@ -54,11 +54,6 @@ export type Config = {
|
|||||||
*/
|
*/
|
||||||
contextOptions?: playwright.BrowserContextOptions;
|
contextOptions?: playwright.BrowserContextOptions;
|
||||||
|
|
||||||
/**
|
|
||||||
* Permissions to grant to the browser context, for example ["clipboard-read", "clipboard-write"].
|
|
||||||
*/
|
|
||||||
permissions?: string[];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chrome DevTools Protocol endpoint to connect to an existing browser instance in case of Chromium family browsers.
|
* Chrome DevTools Protocol endpoint to connect to an existing browser instance in case of Chromium family browsers.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
# Clipboard Permissions Example
|
|
||||||
|
|
||||||
This example demonstrates how to use the new `--permissions` feature to enable clipboard operations without user permission prompts.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### Via Command Line Arguments
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"mcpServers": {
|
|
||||||
"playwright": {
|
|
||||||
"command": "npx",
|
|
||||||
"args": [
|
|
||||||
"@playwright/mcp@latest",
|
|
||||||
"--permissions", "clipboard-read,clipboard-write"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Via Configuration File
|
|
||||||
|
|
||||||
Create a config file `playwright-mcp-config.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"browser": {
|
|
||||||
"permissions": ["clipboard-read", "clipboard-write"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Then use it:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"mcpServers": {
|
|
||||||
"playwright": {
|
|
||||||
"command": "npx",
|
|
||||||
"args": [
|
|
||||||
"@playwright/mcp@latest",
|
|
||||||
"--config", "playwright-mcp-config.json"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Via Environment Variable
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export PLAYWRIGHT_MCP_PERMISSIONS="clipboard-read,clipboard-write"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Clipboard Operations
|
|
||||||
|
|
||||||
Once permissions are granted, you can use clipboard APIs via `browser_evaluate`:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Write to clipboard (no permission prompt)
|
|
||||||
await browser_evaluate({
|
|
||||||
function: "() => navigator.clipboard.writeText('Copy this!')"
|
|
||||||
})
|
|
||||||
|
|
||||||
// Read from clipboard (no permission prompt)
|
|
||||||
await browser_evaluate({
|
|
||||||
function: "() => navigator.clipboard.readText()"
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Supported Permissions
|
|
||||||
|
|
||||||
You can grant multiple permissions as a comma-separated list:
|
|
||||||
|
|
||||||
- `clipboard-read`
|
|
||||||
- `clipboard-write`
|
|
||||||
- `geolocation`
|
|
||||||
- `camera`
|
|
||||||
- `microphone`
|
|
||||||
- `notifications`
|
|
||||||
- And any other [Web API permissions](https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API)
|
|
||||||
|
|
||||||
Example with multiple permissions:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
--permissions "clipboard-read,clipboard-write,geolocation,notifications"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- Permissions are applied when the browser context is created
|
|
||||||
- The clipboard API requires secure contexts (HTTPS) in production environments
|
|
||||||
- Some permissions may not be supported in all browsers or may require additional user activation
|
|
||||||
@@ -113,10 +113,7 @@ class IsolatedContextFactory extends BaseContextFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override async _doCreateContext(browser: playwright.Browser): Promise<playwright.BrowserContext> {
|
protected override async _doCreateContext(browser: playwright.Browser): Promise<playwright.BrowserContext> {
|
||||||
const contextOptions = { ...this.browserConfig.contextOptions };
|
return browser.newContext(this.browserConfig.contextOptions);
|
||||||
if (this.browserConfig.permissions)
|
|
||||||
contextOptions.permissions = this.browserConfig.permissions;
|
|
||||||
return browser.newContext(contextOptions);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,10 +128,7 @@ class CdpContextFactory extends BaseContextFactory {
|
|||||||
|
|
||||||
protected override async _doCreateContext(browser: playwright.Browser): Promise<playwright.BrowserContext> {
|
protected override async _doCreateContext(browser: playwright.Browser): Promise<playwright.BrowserContext> {
|
||||||
if (this.browserConfig.isolated) {
|
if (this.browserConfig.isolated) {
|
||||||
const contextOptions: playwright.BrowserContextOptions = {};
|
return browser.newContext(this.browserConfig.contextOptions);
|
||||||
if (this.browserConfig.permissions)
|
|
||||||
contextOptions.permissions = this.browserConfig.permissions;
|
|
||||||
return browser.newContext(contextOptions);
|
|
||||||
}
|
}
|
||||||
return browser.contexts()[0];
|
return browser.contexts()[0];
|
||||||
}
|
}
|
||||||
@@ -154,10 +148,7 @@ class RemoteContextFactory extends BaseContextFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override async _doCreateContext(browser: playwright.Browser): Promise<playwright.BrowserContext> {
|
protected override async _doCreateContext(browser: playwright.Browser): Promise<playwright.BrowserContext> {
|
||||||
const contextOptions: playwright.BrowserContextOptions = {};
|
return browser.newContext(this.browserConfig.contextOptions);
|
||||||
if (this.browserConfig.permissions)
|
|
||||||
contextOptions.permissions = this.browserConfig.permissions;
|
|
||||||
return browser.newContext(contextOptions);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,8 +177,6 @@ class PersistentContextFactory implements BrowserContextFactory {
|
|||||||
handleSIGINT: false,
|
handleSIGINT: false,
|
||||||
handleSIGTERM: false,
|
handleSIGTERM: false,
|
||||||
};
|
};
|
||||||
if (this.browserConfig.permissions)
|
|
||||||
contextOptions.permissions = this.browserConfig.permissions;
|
|
||||||
const browserContext = await browserType.launchPersistentContext(userDataDir, contextOptions);
|
const browserContext = await browserType.launchPersistentContext(userDataDir, contextOptions);
|
||||||
const close = () => this._closeBrowserContext(browserContext, userDataDir);
|
const close = () => this._closeBrowserContext(browserContext, userDataDir);
|
||||||
return { browserContext, close };
|
return { browserContext, close };
|
||||||
|
|||||||
@@ -173,12 +173,14 @@ export function configFromCLIOptions(cliOptions: CLIOptions): Config {
|
|||||||
if (cliOptions.blockServiceWorkers)
|
if (cliOptions.blockServiceWorkers)
|
||||||
contextOptions.serviceWorkers = 'block';
|
contextOptions.serviceWorkers = 'block';
|
||||||
|
|
||||||
|
if (cliOptions.permissions)
|
||||||
|
contextOptions.permissions = cliOptions.permissions;
|
||||||
|
|
||||||
const result: Config = {
|
const result: Config = {
|
||||||
browser: {
|
browser: {
|
||||||
browserName,
|
browserName,
|
||||||
isolated: cliOptions.isolated,
|
isolated: cliOptions.isolated,
|
||||||
userDataDir: cliOptions.userDataDir,
|
userDataDir: cliOptions.userDataDir,
|
||||||
permissions: cliOptions.permissions,
|
|
||||||
launchOptions,
|
launchOptions,
|
||||||
contextOptions,
|
contextOptions,
|
||||||
cdpEndpoint: cliOptions.cdpEndpoint,
|
cdpEndpoint: cliOptions.cdpEndpoint,
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ test('clipboard permissions support via CLI argument', async ({ startClient, ser
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('clipboard permissions support via config file', async ({ startClient, server }) => {
|
test('clipboard permissions support via config file contextOptions', async ({ startClient, server }) => {
|
||||||
server.setContent('/', `
|
server.setContent('/', `
|
||||||
<html>
|
<html>
|
||||||
<body>
|
<body>
|
||||||
@@ -79,7 +79,9 @@ test('clipboard permissions support via config file', async ({ startClient, serv
|
|||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
browser: {
|
browser: {
|
||||||
permissions: ['clipboard-read', 'clipboard-write']
|
contextOptions: {
|
||||||
|
permissions: ['clipboard-read', 'clipboard-write']
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -166,62 +168,3 @@ test('multiple permissions can be granted', async ({ startClient, server }) => {
|
|||||||
result: '"granted"'
|
result: '"granted"'
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('clipboard permissions via environment variable', async ({ startClient, server }) => {
|
|
||||||
server.setContent('/', `
|
|
||||||
<html>
|
|
||||||
<body>
|
|
||||||
<h1>Environment Variable Test</h1>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`, 'text/html');
|
|
||||||
|
|
||||||
// Set environment variable
|
|
||||||
process.env.PLAYWRIGHT_MCP_PERMISSIONS = 'clipboard-read,clipboard-write';
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { client } = await startClient({ args: [] });
|
|
||||||
|
|
||||||
// Navigate to server page
|
|
||||||
const navigateResponse = await client.callTool({
|
|
||||||
name: 'browser_navigate',
|
|
||||||
arguments: { url: server.PREFIX },
|
|
||||||
});
|
|
||||||
expect(navigateResponse.isError).toBeFalsy();
|
|
||||||
|
|
||||||
// Verify permissions are granted via environment variable
|
|
||||||
const permissionsResponse = await client.callTool({
|
|
||||||
name: 'browser_evaluate',
|
|
||||||
arguments: {
|
|
||||||
function: '() => navigator.permissions.query({ name: "clipboard-write" }).then(result => result.state)'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
expect(permissionsResponse.isError).toBeFalsy();
|
|
||||||
expect(permissionsResponse).toHaveResponse({
|
|
||||||
result: '"granted"'
|
|
||||||
});
|
|
||||||
|
|
||||||
// Test clipboard operations work
|
|
||||||
const writeResponse = await client.callTool({
|
|
||||||
name: 'browser_evaluate',
|
|
||||||
arguments: {
|
|
||||||
function: '() => navigator.clipboard.writeText("env test content")'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
expect(writeResponse.isError).toBeFalsy();
|
|
||||||
|
|
||||||
const readResponse = await client.callTool({
|
|
||||||
name: 'browser_evaluate',
|
|
||||||
arguments: {
|
|
||||||
function: '() => navigator.clipboard.readText()'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
expect(readResponse.isError).toBeFalsy();
|
|
||||||
expect(readResponse).toHaveResponse({
|
|
||||||
result: '"env test content"'
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
// Clean up environment variable
|
|
||||||
delete process.env.PLAYWRIGHT_MCP_PERMISSIONS;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|||||||
Reference in New Issue
Block a user