playwright-stealth-mcp-server 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,6 +9,7 @@ A Model Context Protocol (MCP) server for browser automation using Playwright wi
9
9
  - **Persistent Sessions**: Browser session persists across tool calls for multi-step automation
10
10
  - **Screenshot Support**: Capture page screenshots for visual verification
11
11
  - **Code Execution**: Run arbitrary Playwright code with access to the `page` object
12
+ - **Full Permissions**: All browser permissions (notifications, geolocation, camera, etc.) granted by default for testing web apps
12
13
 
13
14
  ## Installation
14
15
 
@@ -95,6 +96,7 @@ Add to your Claude Desktop config file:
95
96
  | `PROXY_USERNAME` | Proxy authentication username | - |
96
97
  | `PROXY_PASSWORD` | Proxy authentication password | - |
97
98
  | `PROXY_BYPASS` | Comma-separated list of hosts to bypass proxy | - |
99
+ | `BROWSER_PERMISSIONS` | Comma-separated list of browser permissions to grant (see below) | All permissions |
98
100
 
99
101
  ## Available Tools
100
102
 
@@ -275,6 +277,65 @@ The server supports HTTP/HTTPS proxies with optional authentication, making it c
275
277
 
276
278
  **Note:** When proxy is configured, the server performs a health check on startup to verify the proxy connection works. If the health check fails, the server will exit with an error.
277
279
 
280
+ ## Browser Permissions
281
+
282
+ By default, the server grants **all** browser permissions to the browser context. This enables testing of features that require permissions such as:
283
+
284
+ - **Web Push Notifications** (`notifications`)
285
+ - **Geolocation** (`geolocation`)
286
+ - **Camera/Microphone** (`camera`, `microphone`)
287
+ - **Clipboard** (`clipboard-read`, `clipboard-write`)
288
+ - **MIDI devices** (`midi`, `midi-sysex`)
289
+ - **Sensors** (`accelerometer`, `gyroscope`, `magnetometer`, `ambient-light-sensor`)
290
+ - **Other** (`background-sync`, `local-fonts`, `payment-handler`, `storage-access`)
291
+
292
+ ### Constraining Permissions
293
+
294
+ If you need to restrict permissions (e.g., for security testing or to simulate a user who denies permissions), use the `BROWSER_PERMISSIONS` environment variable:
295
+
296
+ ```json
297
+ {
298
+ "mcpServers": {
299
+ "playwright": {
300
+ "command": "npx",
301
+ "args": ["-y", "playwright-stealth-mcp-server"],
302
+ "env": {
303
+ "BROWSER_PERMISSIONS": "notifications,clipboard-read,clipboard-write"
304
+ }
305
+ }
306
+ }
307
+ }
308
+ ```
309
+
310
+ This will grant only the specified permissions. All other permission requests will be denied.
311
+
312
+ **Note:** Invalid permission names are silently ignored with a warning in the server logs. If a permission is not recognized, it will be skipped.
313
+
314
+ **Browser Compatibility:** Permission support varies by browser. This server uses Chromium by default, where all listed permissions are supported. If using Firefox or WebKit, some permissions may not work.
315
+
316
+ ### Testing Web Push Notifications
317
+
318
+ With the default configuration (all permissions granted), you can test web push notifications:
319
+
320
+ ```javascript
321
+ // Check if notifications are supported and granted
322
+ const permission = await page.evaluate(() => Notification.permission);
323
+ console.log('Notification permission:', permission); // Should be "granted"
324
+
325
+ // Request notification permission (will auto-grant)
326
+ await page.evaluate(async () => {
327
+ const result = await Notification.requestPermission();
328
+ console.log('Permission result:', result);
329
+ });
330
+
331
+ // Create a test notification
332
+ await page.evaluate(() => {
333
+ new Notification('Test Notification', {
334
+ body: 'This is a test notification from Playwright',
335
+ });
336
+ });
337
+ ```
338
+
278
339
  ## Development
279
340
 
280
341
  ```bash
package/build/index.js CHANGED
@@ -2,6 +2,41 @@
2
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
3
  import { createMCPServer } from '../shared/index.js';
4
4
  import { logServerStart, logError, logWarning, logInfo } from '../shared/logging.js';
5
+ import { ALL_BROWSER_PERMISSIONS } from '../shared/types.js';
6
+ // =============================================================================
7
+ // PERMISSIONS CONFIGURATION
8
+ // =============================================================================
9
+ /**
10
+ * Parse browser permissions from environment variable.
11
+ * If BROWSER_PERMISSIONS is not set, returns undefined (which means grant all permissions).
12
+ * If BROWSER_PERMISSIONS is set, returns the parsed list of permissions.
13
+ * Invalid permissions are logged as warnings and skipped.
14
+ */
15
+ function parseBrowserPermissions() {
16
+ const permissionsEnv = process.env.BROWSER_PERMISSIONS;
17
+ if (!permissionsEnv) {
18
+ // No constraint specified - will grant all permissions by default
19
+ return undefined;
20
+ }
21
+ const requestedPermissions = permissionsEnv.split(',').map((p) => p.trim().toLowerCase());
22
+ const validPermissions = [];
23
+ const invalidPermissions = [];
24
+ for (const perm of requestedPermissions) {
25
+ if (perm === '')
26
+ continue;
27
+ if (ALL_BROWSER_PERMISSIONS.includes(perm)) {
28
+ validPermissions.push(perm);
29
+ }
30
+ else {
31
+ invalidPermissions.push(perm);
32
+ }
33
+ }
34
+ if (invalidPermissions.length > 0) {
35
+ logWarning('config', `Unknown browser permissions ignored: ${invalidPermissions.join(', ')}. ` +
36
+ `Valid permissions: ${ALL_BROWSER_PERMISSIONS.join(', ')}`);
37
+ }
38
+ return validPermissions;
39
+ }
5
40
  // =============================================================================
6
41
  // PROXY CONFIGURATION
7
42
  // =============================================================================
@@ -116,12 +151,18 @@ function validateEnvironment() {
116
151
  description: 'Comma-separated list of hosts to bypass proxy',
117
152
  defaultValue: undefined,
118
153
  },
154
+ {
155
+ name: 'BROWSER_PERMISSIONS',
156
+ description: 'Comma-separated list of browser permissions to grant. If not set, ALL permissions are granted.',
157
+ defaultValue: 'all (notifications, geolocation, camera, microphone, clipboard-read, etc.)',
158
+ },
119
159
  ];
120
160
  // Log configuration
121
161
  const stealthMode = process.env.STEALTH_MODE === 'true';
122
162
  const headless = process.env.HEADLESS !== 'false';
123
163
  const timeout = process.env.TIMEOUT || '30000';
124
164
  const proxyUrl = process.env.PROXY_URL;
165
+ const browserPermissions = process.env.BROWSER_PERMISSIONS;
125
166
  if (stealthMode) {
126
167
  logWarning('config', 'Stealth mode enabled - using anti-detection measures');
127
168
  // Log stealth-specific configuration
@@ -149,6 +190,12 @@ function validateEnvironment() {
149
190
  logInfo('config', 'Proxy authentication enabled');
150
191
  }
151
192
  }
193
+ if (browserPermissions) {
194
+ logInfo('config', `Browser permissions constrained to: ${browserPermissions}`);
195
+ }
196
+ else {
197
+ logInfo('config', 'Browser permissions: all (default)');
198
+ }
152
199
  // Show optional configuration if DEBUG is set
153
200
  if (process.env.DEBUG) {
154
201
  console.error('\nOptional environment variables:');
@@ -176,7 +223,9 @@ async function main() {
176
223
  validateEnvironment();
177
224
  // Step 2: Build proxy configuration if provided
178
225
  const proxyConfig = buildProxyConfig();
179
- // Step 3: If proxy is configured, perform health check
226
+ // Step 3: Parse browser permissions (undefined = all permissions granted)
227
+ const browserPermissions = parseBrowserPermissions();
228
+ // Step 4: If proxy is configured, perform health check
180
229
  if (proxyConfig) {
181
230
  try {
182
231
  await healthCheckProxy(proxyConfig);
@@ -187,11 +236,14 @@ async function main() {
187
236
  process.exit(1);
188
237
  }
189
238
  }
190
- // Step 4: Create server using factory, passing proxy config
191
- const { server, registerHandlers, cleanup } = createMCPServer(proxyConfig);
192
- // Step 5: Register all handlers (tools)
239
+ // Step 5: Create server using factory, passing proxy config and permissions
240
+ const { server, registerHandlers, cleanup } = createMCPServer({
241
+ proxy: proxyConfig,
242
+ permissions: browserPermissions,
243
+ });
244
+ // Step 6: Register all handlers (tools)
193
245
  await registerHandlers(server);
194
- // Step 6: Set up graceful shutdown
246
+ // Step 7: Set up graceful shutdown
195
247
  const handleShutdown = async () => {
196
248
  logWarning('shutdown', 'Received shutdown signal, closing browser...');
197
249
  await cleanup();
@@ -199,7 +251,7 @@ async function main() {
199
251
  };
200
252
  process.on('SIGINT', handleShutdown);
201
253
  process.on('SIGTERM', handleShutdown);
202
- // Step 7: Start server with stdio transport
254
+ // Step 8: Start server with stdio transport
203
255
  const transport = new StdioServerTransport();
204
256
  await server.connect(transport);
205
257
  const stealthMode = process.env.STEALTH_MODE === 'true';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playwright-stealth-mcp-server",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "Local implementation of Playwright Stealth MCP server",
5
5
  "mcpName": "com.pulsemcp.servers/playwright-stealth",
6
6
  "main": "build/index.js",
package/shared/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
- export { createMCPServer, type IPlaywrightClient, type ClientFactory } from './server.js';
1
+ export { createMCPServer, type IPlaywrightClient, type ClientFactory, type CreateMCPServerOptions, } from './server.js';
2
2
  export { createRegisterTools } from './tools.js';
3
3
  export { registerResources } from './resources.js';
4
- export { logServerStart, logError, logWarning, logDebug } from './logging.js';
4
+ export { logServerStart, logError, logWarning, logDebug, logInfo } from './logging.js';
5
5
  export * from './storage/index.js';
6
+ export * from './types.js';
6
7
  //# sourceMappingURL=index.d.ts.map
package/shared/index.js CHANGED
@@ -1,5 +1,6 @@
1
- export { createMCPServer } from './server.js';
1
+ export { createMCPServer, } from './server.js';
2
2
  export { createRegisterTools } from './tools.js';
3
3
  export { registerResources } from './resources.js';
4
- export { logServerStart, logError, logWarning, logDebug } from './logging.js';
4
+ export { logServerStart, logError, logWarning, logDebug, logInfo } from './logging.js';
5
5
  export * from './storage/index.js';
6
+ export * from './types.js';
@@ -1,5 +1,5 @@
1
1
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
- import type { ExecuteResult, BrowserState, PlaywrightConfig, ProxyConfig } from './types.js';
2
+ import type { ExecuteResult, BrowserState, PlaywrightConfig, ProxyConfig, BrowserPermission } from './types.js';
3
3
  /**
4
4
  * Maximum allowed dimension for screenshots in pixels.
5
5
  * Claude's API rejects images where either dimension exceeds 8000 pixels.
@@ -70,7 +70,16 @@ export declare class PlaywrightClient implements IPlaywrightClient {
70
70
  getConfig(): PlaywrightConfig;
71
71
  }
72
72
  export type ClientFactory = () => IPlaywrightClient;
73
- export declare function createMCPServer(proxyConfig?: ProxyConfig): {
73
+ /**
74
+ * Options for creating the MCP server
75
+ */
76
+ export interface CreateMCPServerOptions {
77
+ /** Proxy configuration for browser connections */
78
+ proxy?: ProxyConfig;
79
+ /** Browser permissions to grant. If undefined, all permissions are granted. */
80
+ permissions?: BrowserPermission[];
81
+ }
82
+ export declare function createMCPServer(options?: CreateMCPServerOptions): {
74
83
  server: Server<{
75
84
  method: string;
76
85
  params?: {
package/shared/server.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
2
  import { createRegisterTools } from './tools.js';
3
3
  import { registerResources } from './resources.js';
4
+ import { logWarning } from './logging.js';
5
+ import { ALL_BROWSER_PERMISSIONS } from './types.js';
4
6
  /**
5
7
  * Maximum allowed dimension for screenshots in pixels.
6
8
  * Claude's API rejects images where either dimension exceeds 8000 pixels.
@@ -74,6 +76,19 @@ export class PlaywrightClient {
74
76
  // which perform HTTPS inspection and may re-sign certificates)
75
77
  ignoreHTTPSErrors: !!this.config.proxy,
76
78
  });
79
+ // Grant browser permissions (defaults to all permissions if not specified)
80
+ const permissionsToGrant = this.config.permissions ?? [...ALL_BROWSER_PERMISSIONS];
81
+ if (permissionsToGrant.length > 0) {
82
+ try {
83
+ await this.context.grantPermissions(permissionsToGrant);
84
+ }
85
+ catch (error) {
86
+ // Some permissions may not be supported in all browsers/versions
87
+ // Log a warning but continue - the browser will still function
88
+ const message = error instanceof Error ? error.message : String(error);
89
+ logWarning('permissions', `Failed to grant some permissions: ${message}`);
90
+ }
91
+ }
77
92
  this.page = await this.context.newPage();
78
93
  // Apply timeout configuration to Playwright
79
94
  this.page.setDefaultTimeout(this.config.timeout);
@@ -172,6 +187,7 @@ export class PlaywrightClient {
172
187
  currentUrl: this.page.url(),
173
188
  title: await this.page.title(),
174
189
  isOpen: true,
190
+ permissions: this.config.permissions ?? [...ALL_BROWSER_PERMISSIONS],
175
191
  };
176
192
  }
177
193
  catch {
@@ -191,11 +207,11 @@ export class PlaywrightClient {
191
207
  return this.config;
192
208
  }
193
209
  }
194
- export function createMCPServer(proxyConfig) {
210
+ export function createMCPServer(options) {
195
211
  const stealthMode = process.env.STEALTH_MODE === 'true';
196
212
  const server = new Server({
197
213
  name: 'playwright-stealth-mcp-server',
198
- version: '0.0.5',
214
+ version: '0.0.7',
199
215
  }, {
200
216
  capabilities: {
201
217
  tools: {},
@@ -221,10 +237,11 @@ export function createMCPServer(proxyConfig) {
221
237
  headless,
222
238
  timeout,
223
239
  navigationTimeout,
224
- proxy: proxyConfig,
240
+ proxy: options?.proxy,
225
241
  stealthUserAgent,
226
242
  stealthMaskLinux,
227
243
  stealthLocale,
244
+ permissions: options?.permissions,
228
245
  });
229
246
  return activeClient;
230
247
  });
package/shared/types.d.ts CHANGED
@@ -1,6 +1,12 @@
1
1
  /**
2
2
  * Types for Playwright Stealth MCP server
3
3
  */
4
+ /**
5
+ * All permissions that Playwright supports granting to browser contexts.
6
+ * Note: Not all permissions work in all browsers or browser versions.
7
+ */
8
+ export declare const ALL_BROWSER_PERMISSIONS: readonly ["accelerometer", "ambient-light-sensor", "background-sync", "camera", "clipboard-read", "clipboard-write", "geolocation", "gyroscope", "local-fonts", "magnetometer", "microphone", "midi", "midi-sysex", "notifications", "payment-handler", "storage-access"];
9
+ export type BrowserPermission = (typeof ALL_BROWSER_PERMISSIONS)[number];
4
10
  /**
5
11
  * Proxy configuration for browser connections
6
12
  * Compatible with BrightData Residential Proxies and other HTTP/HTTPS proxies
@@ -28,6 +34,11 @@ export interface PlaywrightConfig {
28
34
  stealthMaskLinux?: boolean;
29
35
  /** Custom locale for stealth mode (default: 'en-US,en') */
30
36
  stealthLocale?: string;
37
+ /**
38
+ * Browser permissions to grant. Defaults to ALL_BROWSER_PERMISSIONS if not specified.
39
+ * Use BROWSER_PERMISSIONS env var to constrain permissions (comma-separated list).
40
+ */
41
+ permissions?: BrowserPermission[];
31
42
  }
32
43
  export interface ExecuteResult {
33
44
  success: boolean;
@@ -45,5 +56,7 @@ export interface BrowserState {
45
56
  headless?: boolean;
46
57
  /** Whether proxy is enabled for browser connections */
47
58
  proxyEnabled?: boolean;
59
+ /** Permissions granted to the browser context */
60
+ permissions?: BrowserPermission[];
48
61
  }
49
62
  //# sourceMappingURL=types.d.ts.map
package/shared/types.js CHANGED
@@ -1,4 +1,25 @@
1
1
  /**
2
2
  * Types for Playwright Stealth MCP server
3
3
  */
4
- export {};
4
+ /**
5
+ * All permissions that Playwright supports granting to browser contexts.
6
+ * Note: Not all permissions work in all browsers or browser versions.
7
+ */
8
+ export const ALL_BROWSER_PERMISSIONS = [
9
+ 'accelerometer',
10
+ 'ambient-light-sensor',
11
+ 'background-sync',
12
+ 'camera',
13
+ 'clipboard-read',
14
+ 'clipboard-write',
15
+ 'geolocation',
16
+ 'gyroscope',
17
+ 'local-fonts',
18
+ 'magnetometer',
19
+ 'microphone',
20
+ 'midi',
21
+ 'midi-sysex',
22
+ 'notifications',
23
+ 'payment-handler',
24
+ 'storage-access',
25
+ ];