playwright-e2e-mcp 0.1.1 → 0.1.2

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.
Files changed (43) hide show
  1. package/README.md +90 -38
  2. package/dist/http.d.ts +19 -1
  3. package/dist/http.js +98 -9
  4. package/dist/http.js.map +1 -1
  5. package/dist/index.js +11 -3
  6. package/dist/index.js.map +1 -1
  7. package/dist/server.d.ts +13 -0
  8. package/dist/server.js +25 -12
  9. package/dist/server.js.map +1 -1
  10. package/dist/tools/compare-visual-state.js +11 -8
  11. package/dist/tools/compare-visual-state.js.map +1 -1
  12. package/dist/tools/generate-e2e-test.js +46 -10
  13. package/dist/tools/generate-e2e-test.js.map +1 -1
  14. package/dist/tools/inspect-page.js +2 -0
  15. package/dist/tools/inspect-page.js.map +1 -1
  16. package/dist/tools/list-tests.js +8 -1
  17. package/dist/tools/list-tests.js.map +1 -1
  18. package/dist/tools/run-test.js +1 -1
  19. package/dist/tools/run-test.js.map +1 -1
  20. package/dist/tools/shared.d.ts +9 -2
  21. package/dist/tools/shared.js +135 -10
  22. package/dist/tools/shared.js.map +1 -1
  23. package/dist/tools/validate-selector.js +2 -0
  24. package/dist/tools/validate-selector.js.map +1 -1
  25. package/dist/types/index.d.ts +7 -0
  26. package/dist/utils/change-analyzer.js +31 -26
  27. package/dist/utils/change-analyzer.js.map +1 -1
  28. package/dist/utils/image-diff.js +23 -4
  29. package/dist/utils/image-diff.js.map +1 -1
  30. package/dist/utils/path-utils.d.ts +25 -3
  31. package/dist/utils/path-utils.js +159 -7
  32. package/dist/utils/path-utils.js.map +1 -1
  33. package/dist/utils/playwright-runner.d.ts +14 -10
  34. package/dist/utils/playwright-runner.js +128 -30
  35. package/dist/utils/playwright-runner.js.map +1 -1
  36. package/dist/utils/report-parser.js +7 -6
  37. package/dist/utils/report-parser.js.map +1 -1
  38. package/dist/utils/trace-reader.js +23 -6
  39. package/dist/utils/trace-reader.js.map +1 -1
  40. package/dist/utils/url-policy.d.ts +29 -0
  41. package/dist/utils/url-policy.js +99 -0
  42. package/dist/utils/url-policy.js.map +1 -0
  43. package/package.json +1 -1
package/README.md CHANGED
@@ -19,17 +19,19 @@ with unique CSS selectors, and validate the replacement selector before re-runni
19
19
 
20
20
  ## Demo
21
21
 
22
- **Live endpoint** — a real `initialize` + `tools/list` round-trip against
23
- `https://playwright-e2e-mcp.vercel.app/api/mcp`:
22
+ **Hosted endpoint** — what an `initialize` + `tools/list` round-trip against
23
+ `https://playwright-e2e-mcp.vercel.app/api/mcp` returns for a client that sends the
24
+ bearer token (without one, only `list-tests` and `get-failure` are listed):
24
25
 
25
- ![Live endpoint: initialize handshake and all 8 tools](https://raw.githubusercontent.com/trajectiq-ai/E2E/main/docs/demo-endpoint.png)
26
+ ![Token-protected endpoint: initialize handshake and all 8 tools](https://raw.githubusercontent.com/trajectiq-ai/E2E/main/docs/demo-endpoint.png)
26
27
 
27
28
  **A real test run** — `run-test` served over stdio by `npx -y playwright-e2e-mcp`
28
29
  against the bundled `examples/sample-test.spec.ts` (actual output, unedited):
29
30
 
30
31
  ![run-test result: 4 passed, 0 failed, 5.1s](https://raw.githubusercontent.com/trajectiq-ai/E2E/main/docs/demo-run-test.png)
31
32
 
32
- Images are generated from real captured output with `node scripts/gen-demo-images.mjs`.
33
+ Images are rendered with `node scripts/gen-demo-images.mjs`: the run-test card is real
34
+ captured output; the endpoint card is an illustration of the authenticated listing.
33
35
 
34
36
  ## Install
35
37
 
@@ -40,7 +42,7 @@ and every other MCP client — pick whichever route fits:
40
42
  | --- | --- |
41
43
  | **npm (canonical, fastest)** | `npx -y playwright-e2e-mcp` |
42
44
  | **MCP Registry** (registry-aware clients discover it automatically) | `io.github.trajectiq-ai/E2E` — [listing](https://registry.modelcontextprotocol.io/) |
43
- | **Any client, no npm account needed** | `npx -y github:trajectiq-ai/E2E` |
45
+ | **Any client, no npm account needed** | `npx -y github:trajectiq-ai/E2E#v0.1.2` (pin a release tag) |
44
46
  | **Claude Desktop, zero Node setup** | double-click the [`.mcpb` extension](https://github.com/trajectiq-ai/E2E/releases) |
45
47
  | **Remote-only clients (ChatGPT connectors)** | `https://playwright-e2e-mcp.vercel.app/api/mcp` |
46
48
 
@@ -66,7 +68,7 @@ Details and per-client config: [Installation](#installation) ·
66
68
 
67
69
  | Argument | Type | Description |
68
70
  | --- | --- | --- |
69
- | `projectRoot` | string | Project directory (default: server working directory) |
71
+ | `projectRoot` | string | Project directory inside the configured root (default: server working directory) |
70
72
  | `testFiles` | string[] | Files/directories relative to the root; `file:line` supported. Omit to run everything |
71
73
  | `grep` | string | Only run tests whose title matches this regex |
72
74
  | `browser` | `chromium` \| `firefox` \| `webkit` | Playwright project to run (matched against config project names) |
@@ -77,7 +79,7 @@ Details and per-client config: [Installation](#installation) ·
77
79
  | `config` | string | `playwright.config` path **or 1-based index** when the project has several |
78
80
  | `retryOnFailure` | boolean | Auto-retry failures **once** before reporting them (default `true`; ignored when `retries` is set) |
79
81
  | `lastFailed` | boolean | Only re-run tests that failed in the previous run (Playwright `--last-failed`) — the fast fix → re-run loop |
80
- | `args` | string[] | Extra CLI flags (shell metacharacters are rejected) |
82
+ | `args` | string[] | Extra Playwright flags from an allowlist (`--repeat-each=N`, `--max-failures=N`, `--update-snapshots`, `--shard=1/3`, `--trace=on`, …); values go after `=` and are checked, and flags that take a path, such as `--config` or `--output`, are rejected |
81
83
 
82
84
  Flakiness handling: by default the server injects `--retries=1` (unless the config
83
85
  already sets `retries`), so a test that passes on the retry is reported as **flaky**,
@@ -172,9 +174,9 @@ plain CSS.
172
174
  | --- | --- | --- |
173
175
  | `description` | string | What the test should cover (required) |
174
176
  | `pageUrl` | string | Page the test starts on (default: `baseURL` / `webServer.url` from config) |
175
- | `testDir` / `file` | string | Where to write the spec (default: detected `testDir` + `generated/<slug>.spec.ts`) |
177
+ | `testDir` / `file` | string | Where to write the spec (default: detected `testDir` + `generated/<slug>.spec.ts`); `file` must end in `.spec.*` or `.test.*` |
176
178
  | `write` | boolean | Write the file to disk (default `true`) |
177
- | `overwrite` | boolean | Replace an existing file at the target path |
179
+ | `overwrite` | boolean | Replace an existing spec at the target path; only specs this tool generated can be replaced |
178
180
  | `liveInspect` | boolean | Cross-check selectors against the live page (default on when a URL is known) |
179
181
  | `projectRoot` / `config` | string | As with the other tools |
180
182
 
@@ -236,11 +238,12 @@ Requirements:
236
238
  (`npx playwright install chromium`)
237
239
 
238
240
  **No npm account needed** — install straight from GitHub (the `prepare` script
239
- builds `dist/` automatically on install):
241
+ builds `dist/` automatically on install). Pin a release tag: an unpinned
242
+ `github:trajectiq-ai/E2E` runs whatever is on the default branch at that moment.
240
243
 
241
244
  ```bash
242
- npx -y github:trajectiq-ai/E2E
243
- npm install -D github:trajectiq-ai/E2E @playwright/test # or as a project dependency
245
+ npx -y github:trajectiq-ai/E2E#v0.1.2
246
+ npm install -D github:trajectiq-ai/E2E#v0.1.2 @playwright/test # or as a project dependency
244
247
  npx playwright install chromium
245
248
  ```
246
249
 
@@ -248,7 +251,7 @@ Or grab the packaged tarball from the repo's **GitHub Releases** page and instal
248
251
  it locally:
249
252
 
250
253
  ```bash
251
- npm install -D https://github.com/trajectiq-ai/E2E/releases/download/v0.1.0/playwright-e2e-mcp-0.1.0.tgz
254
+ npm install -D https://github.com/trajectiq-ai/E2E/releases/download/v0.1.2/playwright-e2e-mcp-0.1.2.tgz
252
255
  ```
253
256
 
254
257
  Listed in the **official [MCP Registry](https://registry.modelcontextprotocol.io/)** as
@@ -264,7 +267,7 @@ Listed in the **official [MCP Registry](https://registry.modelcontextprotocol.io
264
267
  "mcpServers": {
265
268
  "playwright-e2e": {
266
269
  "command": "npx",
267
- "args": ["-y", "github:trajectiq-ai/E2E"],
270
+ "args": ["-y", "github:trajectiq-ai/E2E#v0.1.2"],
268
271
  "env": { "PW_MCP_PROJECT_ROOT": "/absolute/path/to/your/project" }
269
272
  }
270
273
  }
@@ -278,8 +281,8 @@ when the client launches it somewhere else (e.g. your home directory).
278
281
  **Codex / VS Code / Copilot CLIs:**
279
282
 
280
283
  ```bash
281
- codex mcp add playwright-e2e -- npx -y github:trajectiq-ai/E2E
282
- code --add-mcp '{"name":"playwright-e2e","command":"npx","args":["-y","github:trajectiq-ai/E2E"]}'
284
+ codex mcp add playwright-e2e -- npx -y github:trajectiq-ai/E2E#v0.1.2
285
+ code --add-mcp '{"name":"playwright-e2e","command":"npx","args":["-y","github:trajectiq-ai/E2E#v0.1.2"]}'
283
286
  ```
284
287
 
285
288
  Codex's defaults fight this server: the first launch clones the repo and runs
@@ -290,7 +293,7 @@ Codex's defaults fight this server: the first launch clones the repo and runs
290
293
  ```toml
291
294
  [mcp_servers.playwright-e2e]
292
295
  command = "npx"
293
- args = ["-y", "github:trajectiq-ai/E2E"]
296
+ args = ["-y", "github:trajectiq-ai/E2E#v0.1.2"]
294
297
  startup_timeout_sec = 60
295
298
  tool_timeout_sec = 600
296
299
  ```
@@ -298,13 +301,14 @@ tool_timeout_sec = 600
298
301
  **Claude Desktop (one-click):** download and double-click the `.mcpb` Desktop
299
302
  Extension attached to the [latest release](https://github.com/trajectiq-ai/E2E/releases) —
300
303
  the bundle ships its own dependencies, so no Node setup is required. On install it
301
- prompts once for your **project root** (defaults to your home folder) and wires it
304
+ asks you to pick your **project root** (required, no default: choose the project
305
+ folder, not your home directory) and wires it
302
306
  into `PW_MCP_PROJECT_ROOT`, so the tools point at a real project from the first call.
303
307
 
304
308
  **Claude Code:**
305
309
 
306
310
  ```bash
307
- claude mcp add playwright-e2e -- npx -y github:trajectiq-ai/E2E
311
+ claude mcp add playwright-e2e -- npx -y github:trajectiq-ai/E2E#v0.1.2
308
312
  ```
309
313
 
310
314
  **Gemini CLI / Qwen Code:** paste the `mcpServers` block above into
@@ -345,7 +349,7 @@ Streamable HTTP bridge for exactly that case:
345
349
  | --- | --- |
346
350
  | **Endpoint** | `https://playwright-e2e-mcp.vercel.app/api/mcp` |
347
351
  | **Transport** | MCP Streamable HTTP (`POST` JSON in, JSON or SSE out) |
348
- | **Auth** | none — the URL is public |
352
+ | **Auth** | optional bearer token (`PW_MCP_HTTP_TOKEN`); without one only read-only tools are served |
349
353
  | **Source** | [`api/mcp.ts`](api/mcp.ts) → [`src/http.ts`](src/http.ts) |
350
354
 
351
355
  The bridge runs the *same* `createServer()` as the stdio transport; the SDK
@@ -353,9 +357,22 @@ serves every request with a fresh server instance, which is what a serverless
353
357
  function wants. `test/http-bridge.test.mjs` drives the real Node adapter over
354
358
  `node:http` so a broken bridge fails in CI, not in ChatGPT.
355
359
 
360
+ **Open vs. token-protected.** When the deployment has no `PW_MCP_HTTP_TOKEN`,
361
+ anyone can reach the URL, so the bridge serves only `list-tests` and
362
+ `get-failure`, in restricted mode: `list-tests` scans sources instead of running
363
+ `playwright test --list` (which would execute the project's config), callers
364
+ cannot pick another `projectRoot`, and nothing spawns a process, drives a browser
365
+ or writes a file. Set `PW_MCP_HTTP_TOKEN` (at least 16 characters; use a random
366
+ value) to serve all eight tools to clients that send `Authorization: Bearer <token>`;
367
+ other requests get `401`. Child processes started over HTTP get only an allowlisted
368
+ environment. `PW_MCP_ALLOWED_HOSTS` (comma separated, `*` for any) limits the
369
+ accepted `Host` header; without a token and without that variable, only
370
+ `localhost` names and the deployment's own Vercel hostnames are accepted
371
+ (DNS-rebinding protection).
372
+
356
373
  **Add it to ChatGPT:** Settings → Connectors → turn on **Advanced → Developer
357
374
  mode** → *Create custom connector* → paste the endpoint above → authentication
358
- **None**.
375
+ **None** (read-only tools).
359
376
 
360
377
  Codex can also take the remote transport instead of spawning `npx`, if you'd
361
378
  rather not ship Playwright to every machine:
@@ -365,11 +382,11 @@ codex mcp add playwright-e2e-remote --url https://playwright-e2e-mcp.vercel.app/
365
382
  ```
366
383
 
367
384
  **What to expect:** `list-tests` works and reports the specs bundled with the
368
- deployment. Tools that spawn a browser (`run-test`, `inspect-page`,
369
- `validate-selector`, `diagnose-flaky`, …) cannot download Chromium in a
370
- serverless function, so they return their normal `NO_PLAYWRIGHT` hint. Use the
371
- stdio install for real runs; the hosted endpoint is for discovery and for
372
- clients that cannot run local processes.
385
+ deployment. Even with a token, tools that spawn a browser (`run-test`,
386
+ `inspect-page`, `validate-selector`, `diagnose-flaky`, …) cannot download
387
+ Chromium in a serverless function, so they return their normal `NO_PLAYWRIGHT`
388
+ hint. Use the stdio install for real runs; the hosted endpoint is for discovery
389
+ and for clients that cannot run local processes.
373
390
 
374
391
  ```bash
375
392
  # verify the handshake without any client
@@ -379,17 +396,21 @@ curl -X POST https://playwright-e2e-mcp.vercel.app/api/mcp \
379
396
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'
380
397
  ```
381
398
 
382
- Redeploy after a change:
383
-
384
- ```bash
385
- npx vercel deploy --yes --prod --token="$VERCEL_TOKEN"
386
- ```
399
+ Redeploy after a change: merge to `main`. Vercel's Git integration deploys
400
+ every push, so there is no token to manage and no CLI step — watch the
401
+ `Vercel` commit status for the deployment result.
387
402
 
388
403
  ## Configuration
389
404
 
390
405
  | Environment variable | Default | Purpose |
391
406
  | --- | --- | --- |
392
407
  | `PW_MCP_PROJECT_ROOT` | server cwd | Default project root for every tool |
408
+ | `PW_MCP_ALLOWED_ROOTS` | — | Extra directories a caller may pass as `projectRoot` (`:`-separated, `;` on Windows). Anything outside these and the default root is rejected |
409
+ | `PW_MCP_HTTP_TOKEN` | — | HTTP bridge only: bearer token (16+ characters) that unlocks all tools (see above) |
410
+ | `PW_MCP_ALLOWED_HOSTS` | localhost + Vercel hostnames when there is no token | HTTP bridge only: comma-separated `Host` allowlist; `*` accepts any |
411
+ | `PW_MCP_PASSTHROUGH_ENV` | — | HTTP bridge only: comma-separated extra variables passed to test runs (e.g. `BASE_URL`) |
412
+ | `PW_MCP_MAX_CHILDREN` | `4` | HTTP bridge only: how many test runs and browser probes may run at once |
413
+ | `PW_MCP_BLOCK_PRIVATE_URLS` | off (on for the HTTP bridge) | `1` makes the URL tools refuse loopback, private-network and cloud-metadata addresses, including redirects and subresources |
393
414
  | `LOG_LEVEL` | `info` | `debug` \| `info` \| `warn` \| `error` \| `silent` |
394
415
  | `LOG_FORMAT` | `text` | `text` or `json` (structured) |
395
416
 
@@ -430,15 +451,42 @@ Logs always go to **stderr** — stdout is reserved for the MCP protocol.
430
451
  | Syntax error in a spec | `SYNTAX_ERROR` with file:line; nothing crashes; `list-tests` falls back to a source scan |
431
452
  | MCP client disconnects | Per-request `AbortSignal` kills the run; stdin end triggers shutdown, and every tracked child tree is force-killed (`killActiveChildren`) |
432
453
  | Disk full | `ENOSPC` detected → `DISK_FULL` with a "free space" hint; logging never throws |
433
- | Malicious paths | `../../etc/passwd`, absolute paths outside the root, URLs and null bytes are rejected with `INVALID_PATH`; CLI args are shell-metacharacter-checked |
454
+ | Malicious paths | `../../etc/passwd`, absolute paths outside the root, a `projectRoot` outside the allowed roots, symlinks that leave the root, URLs and null bytes are rejected with `INVALID_PATH`; extra CLI args must be allowlisted Playwright flags |
434
455
 
435
456
  ## Security notes
436
457
 
437
458
  - **No shell**: Playwright is spawned as `node <playwright/cli.js> …` with an argument
438
- array — no command interpolation.
439
- - **Path sandbox**: user paths are resolved lexically and must stay inside the project root.
459
+ array — no command interpolation. Extra `args` are limited to an allowlist of
460
+ Playwright flags, each with a checked value (`--flag=value`), so a caller cannot
461
+ swap in another `--config` or `--output` or hand git an option through
462
+ `--only-changed`. Paths with a segment starting with `-` are rejected, so a test
463
+ file name cannot be read as a flag.
464
+ - **Path sandbox**: user paths must stay inside the project root, checked lexically
465
+ and again with symlinks resolved. A caller-supplied `projectRoot` must sit inside
466
+ `PW_MCP_PROJECT_ROOT` (or a `PW_MCP_ALLOWED_ROOTS` entry).
467
+ - **Generated code**: `generate-e2e-test` only writes `*.spec.*` / `*.test.*` files,
468
+ only overwrites specs whose header it wrote itself, never writes through a symlink,
469
+ and escapes every value it puts into strings or comments.
470
+ - **HTTP bridge**: read-only tools unless `PW_MCP_HTTP_TOKEN` is set; child processes
471
+ started over HTTP get only an allowlisted environment (`PATH`, `HOME`, temp dirs,
472
+ locale, `CI`, `PLAYWRIGHT_*`, `npm_config_*` without embedded credentials, plus
473
+ anything in `PW_MCP_PASSTHROUGH_ENV`), at most `PW_MCP_MAX_CHILDREN` run at once
474
+ (a disconnected client frees its slot immediately), and internal error messages
475
+ are not returned to clients. The allowlist only covers the child's own
476
+ environment: test code runs as the same OS user, so on Linux it could still read
477
+ the server's startup environment from `/proc`. That is why only token holders
478
+ can run project code; keep other secrets out of the bridge's environment, or run
479
+ it under a separate user.
480
+ - **SSRF guard**: on the HTTP bridge (or with `PW_MCP_BLOCK_PRIVATE_URLS=1`) the URL tools
481
+ refuse hosts that resolve to loopback, private, link-local/metadata or reserved
482
+ addresses, and route the browser through a local proxy that applies the same check
483
+ to every redirect hop and subresource; IPv6 forms that embed an IPv4 address
484
+ (mapped, NAT64, 6to4, Teredo) are blocked too, and WebRTC UDP is disabled so a
485
+ page cannot reach the network around the proxy. It is off for stdio by default because
486
+ opening `http://localhost` dev servers is what these tools are for.
440
487
  - **Cleanup**: temp report/script files are written to the OS temp dir and removed;
441
- child processes are tracked and killed on shutdown.
488
+ child processes are tracked and killed on shutdown. Trace decompression has one
489
+ size budget per archive, and PNGs are capped at 16,384 px per side and 50 M pixels.
442
490
 
443
491
  ## Development
444
492
 
@@ -459,7 +507,7 @@ Zod v4 standard schemas; every tool declares spec tool annotations.
459
507
  ```bash
460
508
  npm install
461
509
  npm run build # tsc → dist/ (zero errors)
462
- npm test # build + test/run-tests.mjs (70 unit tests, any Node ≥20)
510
+ npm test # build + test/run-tests.mjs (unit tests, any Node ≥20)
463
511
  npm run e2e # build + e2e/run.mjs: live MCP ↔ Playwright integration suite
464
512
  ```
465
513
 
@@ -469,8 +517,12 @@ discovery, multiple configs, missing install, test-file scanning), the shared to
469
517
  helpers, the **trace reader** (synthetic trace.zip: error, failed action, DOM snapshot,
470
518
  `*.network` failed-request parsing, console error/warning events), the **image diff**
471
519
  (PNG round-trip, regions, color shift, dimension changes), the **change analyzer**
472
- (selector extraction, git + mtime paths) and the **flaky verdict logic**
473
- (failure signatures, CONSISTENTLY FAILING / FLAKY / NOT REPRODUCING / NO TESTS RAN).
520
+ (selector extraction, git + mtime paths), the **flaky verdict logic**
521
+ (failure signatures, CONSISTENTLY FAILING / FLAKY / NOT REPRODUCING / NO TESTS RAN),
522
+ the **HTTP bridge** (token, restricted mode, Host allowlist), the **security
523
+ regressions** (sandbox escapes, argument smuggling, symlink writes, code injection,
524
+ SSRF ranges, env scrubbing, decode limits), the `.mcpb` manifest and the MCP config
525
+ files.
474
526
 
475
527
  ### Integration suite (`npm run e2e`)
476
528
 
package/dist/http.d.ts CHANGED
@@ -16,8 +16,26 @@ import type { IncomingMessage, ServerResponse } from 'node:http';
16
16
  import type { McpHttpHandler } from '@modelcontextprotocol/server';
17
17
  /** Mirrors the SDK's default POST body bound, so oversized bodies die early. */
18
18
  export declare const MAX_BODY_BYTES: number;
19
+ export interface McpHttpHandlerOptions {
20
+ /**
21
+ * Bearer token clients must send (`Authorization: Bearer <token>`).
22
+ * Defaults to PW_MCP_HTTP_TOKEN. With a token every tool is served;
23
+ * without one only READ_ONLY_TOOLS are, in restricted mode, so an open
24
+ * endpoint can never spawn processes, drive a browser or write files.
25
+ */
26
+ token?: string;
27
+ /**
28
+ * Hostnames accepted in the Host header (DNS-rebinding protection).
29
+ * Defaults to PW_MCP_ALLOWED_HOSTS (comma separated; `*` allows any).
30
+ * When unset and there is no token, only localhost names and the
31
+ * deployment's own Vercel hostnames are accepted.
32
+ */
33
+ allowedHosts?: string[];
34
+ }
35
+ /** Tokens shorter than this are refused: they can be guessed. */
36
+ export declare const MIN_TOKEN_LENGTH = 16;
19
37
  /** Build the fetch-shaped MCP handler backed by a fresh server per request. */
20
- export declare function createMcpHttpHandler(): McpHttpHandler;
38
+ export declare function createMcpHttpHandler(options?: McpHttpHandlerOptions): McpHttpHandler;
21
39
  /**
22
40
  * Serve one Node request through the MCP handler.
23
41
  *
package/dist/http.js CHANGED
@@ -12,17 +12,90 @@
12
12
  * The stdio transport in server.ts is untouched; this is an additional way to
13
13
  * reach the same eight tools.
14
14
  */
15
- import { createMcpHandler } from '@modelcontextprotocol/server';
16
- import { createServer } from './server.js';
15
+ import { createHash, timingSafeEqual } from 'node:crypto';
16
+ import { createMcpHandler, hostHeaderValidationResponse } from '@modelcontextprotocol/server';
17
+ import { createServer, READ_ONLY_TOOLS } from './server.js';
18
+ import { setMaxChildren, setScrubChildEnv } from './utils/playwright-runner.js';
19
+ import { setBlockPrivateUrls } from './utils/url-policy.js';
17
20
  import { logger } from './utils/logger.js';
18
21
  /** Mirrors the SDK's default POST body bound, so oversized bodies die early. */
19
22
  export const MAX_BODY_BYTES = 4 * 1024 * 1024;
23
+ /** Tokens shorter than this are refused: they can be guessed. */
24
+ export const MIN_TOKEN_LENGTH = 16;
25
+ /** Default Host allowlist for an open (token-less) bridge. */
26
+ function defaultAllowedHosts() {
27
+ const vercel = [process.env.VERCEL_URL, process.env.VERCEL_BRANCH_URL, process.env.VERCEL_PROJECT_PRODUCTION_URL]
28
+ .map((host) => host?.trim().toLowerCase())
29
+ .filter((host) => Boolean(host));
30
+ return ['localhost', '127.0.0.1', '[::1]', ...vercel];
31
+ }
32
+ function envList(value) {
33
+ return (value ?? '')
34
+ .split(',')
35
+ .map((item) => item.trim())
36
+ .filter((item) => item !== '');
37
+ }
38
+ /** Constant-time comparison of the presented bearer token (hashed, so length does not leak). */
39
+ function tokenMatches(header, token) {
40
+ const match = /^Bearer\s+(.+)$/i.exec(header?.trim() ?? '');
41
+ if (!match)
42
+ return false;
43
+ const digest = (value) => createHash('sha256').update(value).digest();
44
+ return timingSafeEqual(digest(match[1].trim()), digest(token));
45
+ }
46
+ /** PW_MCP_MAX_CHILDREN as a positive integer; anything else means the default of 4. */
47
+ function maxChildrenFromEnv() {
48
+ const value = Number(process.env.PW_MCP_MAX_CHILDREN ?? '');
49
+ return Number.isInteger(value) && value > 0 ? value : 4;
50
+ }
51
+ function jsonResponse(status, body, headers = {}) {
52
+ return new Response(JSON.stringify(body), {
53
+ status,
54
+ headers: { 'content-type': 'application/json', ...headers },
55
+ });
56
+ }
20
57
  /** Build the fetch-shaped MCP handler backed by a fresh server per request. */
21
- export function createMcpHttpHandler() {
22
- return createMcpHandler(() => createServer(), {
58
+ export function createMcpHttpHandler(options = {}) {
59
+ const token = (options.token ?? process.env.PW_MCP_HTTP_TOKEN ?? '').trim();
60
+ if (token && token.length < MIN_TOKEN_LENGTH) {
61
+ throw new Error(`PW_MCP_HTTP_TOKEN must be at least ${MIN_TOKEN_LENGTH} characters (use a random value).`);
62
+ }
63
+ const configuredHosts = options.allowedHosts ?? envList(process.env.PW_MCP_ALLOWED_HOSTS);
64
+ const allowedHosts = configuredHosts.includes('*')
65
+ ? []
66
+ : configuredHosts.length > 0 || token
67
+ ? configuredHosts
68
+ : defaultAllowedHosts();
69
+ // Children spawned on behalf of HTTP callers must not see deployment
70
+ // secrets, and URL tools must not reach the deployment's private network.
71
+ setScrubChildEnv(true);
72
+ setMaxChildren(maxChildrenFromEnv());
73
+ setBlockPrivateUrls(true);
74
+ if (!token) {
75
+ logger.warn('PW_MCP_HTTP_TOKEN is not set; serving read-only tools only', { tools: READ_ONLY_TOOLS });
76
+ }
77
+ const inner = createMcpHandler((context) => createServer({
78
+ ...(token ? {} : { tools: READ_ONLY_TOOLS, restricted: true }),
79
+ // Lets tools stop their children when the HTTP client disconnects.
80
+ requestSignal: context?.requestInfo?.signal,
81
+ }), {
23
82
  legacy: 'stateless',
24
83
  onerror: (error) => logger.error('mcp http handler error', { error }),
25
84
  });
85
+ return {
86
+ ...inner,
87
+ fetch: async (request, requestOptions) => {
88
+ if (allowedHosts.length > 0) {
89
+ const rejected = hostHeaderValidationResponse(request, allowedHosts);
90
+ if (rejected)
91
+ return rejected;
92
+ }
93
+ if (token && !tokenMatches(request.headers.get('authorization'), token)) {
94
+ return jsonResponse(401, { error: 'unauthorized' }, { 'www-authenticate': 'Bearer' });
95
+ }
96
+ return inner.fetch(request, requestOptions);
97
+ },
98
+ };
26
99
  }
27
100
  /** A failure that maps onto an HTTP status before any response is written. */
28
101
  class RequestError extends Error {
@@ -59,17 +132,18 @@ async function readBody(req) {
59
132
  const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
60
133
  size += buf.byteLength;
61
134
  if (size > MAX_BODY_BYTES) {
62
- req.destroy();
135
+ // Stop reading; the caller answers 413 and then closes the connection.
136
+ req.pause();
63
137
  throw new RequestError(413, 'request body too large');
64
138
  }
65
139
  chunks.push(buf);
66
140
  }
67
141
  return Buffer.concat(chunks, size);
68
142
  }
69
- function buildRequest(req, method, body) {
143
+ function buildRequest(req, method, body, signal) {
70
144
  const protocol = (firstHeader(req.headers['x-forwarded-proto']) ?? 'http').split(',')[0].trim();
71
145
  const host = firstHeader(req.headers.host) ?? 'localhost';
72
- const init = { method, headers: toHeaders(req.headers) };
146
+ const init = { method, headers: toHeaders(req.headers), signal };
73
147
  if (body && body.byteLength > 0)
74
148
  init.body = body;
75
149
  return new Request(`${protocol}://${host}${req.url ?? '/'}`, init);
@@ -93,11 +167,18 @@ function waitDrain(res) {
93
167
  * mid-stream failure closes the socket so the client stops waiting.
94
168
  */
95
169
  export async function handleNodeRequest(handler, req, res) {
170
+ // Abort the request (and the tool's children) when the client goes away
171
+ // before the response is complete, so its child slot is freed at once.
172
+ const disconnect = new AbortController();
173
+ res.once('close', () => {
174
+ if (!res.writableFinished)
175
+ disconnect.abort();
176
+ });
96
177
  let response;
97
178
  try {
98
179
  const method = (req.method ?? 'GET').toUpperCase();
99
180
  const body = method === 'GET' || method === 'HEAD' ? undefined : await readBody(req);
100
- response = await handler.fetch(buildRequest(req, method, body));
181
+ response = await handler.fetch(buildRequest(req, method, body, disconnect.signal));
101
182
  }
102
183
  catch (err) {
103
184
  const status = err instanceof RequestError ? err.status : 500;
@@ -107,7 +188,15 @@ export async function handleNodeRequest(handler, req, res) {
107
188
  if (!res.headersSent && !res.writableEnded) {
108
189
  res.statusCode = status;
109
190
  res.setHeader('content-type', 'application/json');
110
- res.end(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }));
191
+ // Only our own RequestError messages are meant for clients; anything
192
+ // else may carry paths or internals, so it stays in the server log.
193
+ const message = err instanceof RequestError ? err.message : 'internal server error';
194
+ if (status === 413)
195
+ res.setHeader('connection', 'close');
196
+ res.end(JSON.stringify({ error: message }), () => {
197
+ if (status === 413)
198
+ req.destroy();
199
+ });
111
200
  }
112
201
  else if (!res.writableEnded) {
113
202
  res.destroy();
package/dist/http.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -33,9 +33,17 @@ async function main() {
33
33
  'Usage: playwright-e2e-mcp [--version] [--help]',
34
34
  '',
35
35
  'Connects over stdio (MCP). Configuration via environment:',
36
- ' PW_MCP_PROJECT_ROOT default project root (default: cwd)',
37
- ' LOG_LEVEL debug | info | warn | error | silent (default: info)',
38
- ' LOG_FORMAT text | json (default: text)',
36
+ ' PW_MCP_PROJECT_ROOT default project root (default: cwd)',
37
+ ' PW_MCP_ALLOWED_ROOTS extra roots callers may pass as projectRoot',
38
+ ' PW_MCP_BLOCK_PRIVATE_URLS 1 = URL tools refuse private/loopback/metadata addresses',
39
+ ' LOG_LEVEL / MCP_LOG_LEVEL debug | info | warn | error | silent (default: info)',
40
+ ' LOG_FORMAT / MCP_LOG_FORMAT text | json (default: text)',
41
+ '',
42
+ 'HTTP bridge only (api/mcp.ts, src/http.ts):',
43
+ ' PW_MCP_HTTP_TOKEN bearer token (16+ chars) that unlocks all tools',
44
+ ' PW_MCP_ALLOWED_HOSTS Host allowlist, comma separated (* = any)',
45
+ ' PW_MCP_PASSTHROUGH_ENV extra variables passed to test runs',
46
+ ' PW_MCP_MAX_CHILDREN concurrent runs (default: 4)',
39
47
  '',
40
48
  ].join('\n'));
41
49
  return;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,SAAS,YAAY;IACnB,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,aAAa,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACtE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,cAAc,IAAI,CAAC,CAAC;QAC5C,OAAO;IACT,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnE,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB;YACE,4EAA4E;YAC5E,EAAE;YACF,gDAAgD;YAChD,EAAE;YACF,2DAA2D;YAC3D,6DAA6D;YAC7D,8EAA8E;YAC9E,qDAAqD;YACrD,EAAE;SACH,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;QACF,OAAO;IACT,CAAC;IAED,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;IACpD,MAAM,WAAW,EAAE,CAAC;AACtB,CAAC;AAED,IAAI,YAAY,EAAE,EAAE,CAAC;IACnB,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;QAC5B,MAAM,GAAG,GAAG,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE,EAAE,CAAC,CAAC;QACvE,GAAG,CAAC,KAAK,CAAC,wBAAwB,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACpF,OAAO,EAAE,MAAM,EAAE,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,SAAS,YAAY;IACnB,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,aAAa,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACtE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,cAAc,IAAI,CAAC,CAAC;QAC5C,OAAO;IACT,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnE,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB;YACE,4EAA4E;YAC5E,EAAE;YACF,gDAAgD;YAChD,EAAE;YACF,2DAA2D;YAC3D,kEAAkE;YAClE,0EAA0E;YAC1E,uFAAuF;YACvF,mFAAmF;YACnF,4DAA4D;YAC5D,EAAE;YACF,6CAA6C;YAC7C,8EAA8E;YAC9E,wEAAwE;YACxE,kEAAkE;YAClE,2DAA2D;YAC3D,EAAE;SACH,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;QACF,OAAO;IACT,CAAC;IAED,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;IACpD,MAAM,WAAW,EAAE,CAAC;AACtB,CAAC;AAED,IAAI,YAAY,EAAE,EAAE,CAAC;IACnB,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;QAC5B,MAAM,GAAG,GAAG,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE,EAAE,CAAC,CAAC;QACvE,GAAG,CAAC,KAAK,CAAC,wBAAwB,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACpF,OAAO,EAAE,MAAM,EAAE,CAAC"}
package/dist/server.d.ts CHANGED
@@ -14,7 +14,20 @@ export interface CreateServerOptions {
14
14
  projectRoot?: string;
15
15
  /** Share a store across servers (tests). */
16
16
  store?: ToolStore;
17
+ /** Register only these tools (default: all). Used by the hosted HTTP bridge. */
18
+ tools?: readonly string[];
19
+ /** Unauthenticated HTTP mode: see ToolContext.restricted. */
20
+ restricted?: boolean;
21
+ /** Aborted when the HTTP client goes away; combined with the MCP request signal. */
22
+ requestSignal?: AbortSignal;
17
23
  }
24
+ /**
25
+ * Tools that neither drive a browser nor write files. The HTTP bridge
26
+ * serves only these unless it is protected by a token, and runs them in
27
+ * restricted mode, where list-tests scans sources instead of spawning
28
+ * Playwright (which would execute the project's config and spec files).
29
+ */
30
+ export declare const READ_ONLY_TOOLS: readonly string[];
18
31
  export declare function resolveDefaultProjectRoot(): string;
19
32
  export declare function createServer(options?: CreateServerOptions): McpServer;
20
33
  /** Close the server, kill orphaned children, and exit. */