patchright-cli 0.6.0__tar.gz → 0.8.0__tar.gz

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 (54) hide show
  1. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/.claude-plugin/marketplace.json +1 -1
  2. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/.gitignore +2 -0
  3. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/CLAUDE.md +2 -2
  4. patchright_cli-0.8.0/GLOSSARY.md +58 -0
  5. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/PKG-INFO +56 -7
  6. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/README.md +51 -5
  7. patchright_cli-0.8.0/docs/mcp-architecture.svg +120 -0
  8. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/pyproject.toml +6 -1
  9. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/skills/patchright-cli/SKILL.md +171 -12
  10. patchright_cli-0.8.0/skills/patchright-cli/references/session-management.md +172 -0
  11. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/skills/patchright-cli/references/snapshot-refs.md +56 -0
  12. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/skills/patchright-cli/references/video-recording.md +31 -0
  13. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/src/patchright_cli/__init__.py +1 -1
  14. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/src/patchright_cli/cli.py +334 -17
  15. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/src/patchright_cli/daemon.py +1291 -149
  16. patchright_cli-0.8.0/src/patchright_cli/dashboard.py +443 -0
  17. patchright_cli-0.8.0/src/patchright_cli/mcp_server.py +1325 -0
  18. patchright_cli-0.8.0/src/patchright_cli/ref_registry.py +316 -0
  19. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/src/patchright_cli/snapshot.py +1 -1
  20. patchright_cli-0.8.0/tests/test_annotate.py +357 -0
  21. patchright_cli-0.8.0/tests/test_attach.py +455 -0
  22. patchright_cli-0.8.0/tests/test_cli_config.py +630 -0
  23. patchright_cli-0.8.0/tests/test_daemon_handlers.py +1239 -0
  24. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/tests/test_e2e.py +94 -0
  25. patchright_cli-0.8.0/tests/test_mcp.py +172 -0
  26. patchright_cli-0.8.0/tests/test_packaging.py +39 -0
  27. patchright_cli-0.8.0/tests/test_profiles.py +430 -0
  28. patchright_cli-0.8.0/tests/test_ref_registry.py +522 -0
  29. patchright_cli-0.8.0/tests/test_tabs.py +401 -0
  30. patchright_cli-0.8.0/uv.lock +2953 -0
  31. patchright_cli-0.6.0/skills/patchright-cli/references/session-management.md +0 -106
  32. patchright_cli-0.6.0/src/patchright_cli/dashboard.py +0 -135
  33. patchright_cli-0.6.0/src/patchright_cli/ref_registry.py +0 -106
  34. patchright_cli-0.6.0/tests/test_cli_config.py +0 -203
  35. patchright_cli-0.6.0/tests/test_daemon_handlers.py +0 -543
  36. patchright_cli-0.6.0/tests/test_ref_registry.py +0 -137
  37. patchright_cli-0.6.0/uv.lock +0 -1154
  38. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/.claude-plugin/skills/browser-automation.json +0 -0
  39. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/.github/workflows/ci.yml +0 -0
  40. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/.github/workflows/publish.yml +0 -0
  41. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/.pre-commit-config.yaml +0 -0
  42. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/CONTRIBUTING.md +0 -0
  43. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/LICENSE +0 -0
  44. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/Makefile +0 -0
  45. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/docs/installation.md +0 -0
  46. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/patchright-cli +0 -0
  47. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/patchright-cli.cmd +0 -0
  48. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/skills/patchright-cli/references/request-mocking.md +0 -0
  49. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/skills/patchright-cli/references/running-code.md +0 -0
  50. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/skills/patchright-cli/references/storage-state.md +0 -0
  51. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/skills/patchright-cli/references/tracing.md +0 -0
  52. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/src/patchright_cli/__main__.py +0 -0
  53. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/tests/__init__.py +0 -0
  54. {patchright_cli-0.6.0 → patchright_cli-0.8.0}/tests/fixture.html +0 -0
@@ -5,7 +5,7 @@
5
5
  },
6
6
  "metadata": {
7
7
  "description": "Anti-detect browser automation CLI & Skills for AI agents, powered by Patchright",
8
- "version": "0.5.0"
8
+ "version": "0.8.0"
9
9
  },
10
10
  "plugins": [
11
11
  {
@@ -8,3 +8,5 @@ build/
8
8
  docs/reddit-post.md
9
9
  docs/superpowers/
10
10
  .worktrees/
11
+ .claude/settings.local.json
12
+ =*
@@ -57,10 +57,10 @@ CLI (cli.py) --TCP:9321--> Daemon (daemon.py) --Patchright--> Chrome
57
57
 
58
58
  - Always uses persistent browser context (`launch_persistent_context`) with real Chrome (`channel="chrome"`), not Chromium
59
59
  - Headed by default (headless is more detectable)
60
- - Default daemon port: 9321, profiles stored at `~/.patchright-cli/profiles/<session-name>`
60
+ - Default daemon port: 9321, profiles stored at `~/.patchright-cli/profiles/<profile-name>`; a session's profile defaults to its session name (`--profile=<name>` overrides, a value with `/` or `\` is a directory). One session per profile at a time
61
61
  - CLI-daemon protocol: length-prefixed (4-byte big-endian `!I`) JSON over TCP
62
62
  - CLI parses argv manually in `main()` — click is only used for `click.echo()`, not subcommands. New commands must be added to both `COMMANDS_HELP` dict and the `handle_command()` if/elif chain in daemon.py
63
- - Version must be updated in both `pyproject.toml` and `src/patchright_cli/__init__.py` (then run `uv lock` to sync the lock file)
63
+ - Version must be updated in `pyproject.toml`, `src/patchright_cli/__init__.py` and `.claude-plugin/marketplace.json` (then run `uv lock` to sync the lock file; `tests/test_packaging.py` checks they match)
64
64
  - `python -m patchright_cli` works via `__main__.py`
65
65
  - Uses hatchling as build backend, ruff for linting (line-length=120, target py310)
66
66
  - Ruff rules: E, F, I, W, UP, B, SIM (ignores E501, SIM105, E402)
@@ -0,0 +1,58 @@
1
+ # patchright-cli
2
+
3
+ An anti-detect browser automation CLI: callers drive real Chrome through short commands sent to a long-running daemon.
4
+
5
+ ## Sessions and browsers
6
+
7
+ **Session**:
8
+ A named handle on one browser context that callers drive with commands; selected with `-s`.
9
+ _Avoid_: Browser, instance, profile
10
+
11
+ **Default Session**:
12
+ The Session used when no `-s` is given.
13
+
14
+ **Launched Session**:
15
+ A Session whose Chrome patchright-cli started itself and therefore owns outright.
16
+
17
+ **Profile**:
18
+ A named, persistent store of browser state — cookies, storage, logins, history — that a Launched Session runs on; at most one Session uses a Profile at a time. Unless told otherwise, a Session's Profile is named after the Session.
19
+ _Avoid_: User data dir, profile directory, persistent context
20
+
21
+ **Attached Session**:
22
+ A Session connected to a Chrome that something else started, reached over CDP.
23
+ _Avoid_: Connected session, remote session
24
+
25
+ **Host**:
26
+ Whatever started and owns the Chrome an Attached Session connects to — a person, a script, or a product such as a workspace manager.
27
+ _Avoid_: External browser, owner
28
+
29
+ **Host Context**:
30
+ The Host's own browser context, carrying its profile's cookies, storage and logins; at most one Session may use a given Host Context.
31
+ _Avoid_: Default context, existing context, persistent context
32
+
33
+ **Isolated Context**:
34
+ A fresh browser context an Attached Session creates inside the Host's Chrome, sharing none of the Host's state.
35
+ _Avoid_: Incognito context
36
+
37
+ ## Tabs and pages
38
+
39
+ **Page**:
40
+ One browser tab as Chrome sees it.
41
+ _Avoid_: Tab (except in the playwright-cli-compatible `tab-*` command names, which act on Pages)
42
+
43
+ **Tab**:
44
+ A named workspace inside a Session — its own Page, element refs and navigation history — selected with `--tab`.
45
+ _Avoid_: Lane, worker
46
+
47
+ **Default Tab**:
48
+ The Tab used when no `--tab` is given; it follows whichever Page is currently selected rather than owning one.
49
+
50
+ ## Ending a Session
51
+
52
+ **Close**:
53
+ Ending a Session by destroying what it owns: its Tabs' Pages and, for a context it owns, the context itself.
54
+ _Avoid_: Quit, kill (except `kill-all`)
55
+
56
+ **Detach**:
57
+ Letting go of an Attached Session while leaving the Host's Chrome, and anything the Host owns, untouched.
58
+ _Avoid_: Disconnect, release
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: patchright-cli
3
- Version: 0.6.0
3
+ Version: 0.8.0
4
4
  Summary: Anti-detect browser automation CLI using Patchright (undetected Playwright fork)
5
5
  Project-URL: Homepage, https://github.com/AhaiMk01/patchright-cli
6
6
  Project-URL: Repository, https://github.com/AhaiMk01/patchright-cli
@@ -26,9 +26,12 @@ Requires-Dist: click
26
26
  Requires-Dist: patchright
27
27
  Requires-Dist: pyyaml
28
28
  Provides-Extra: dev
29
+ Requires-Dist: fastmcp<5,>=3.0; extra == 'dev'
29
30
  Requires-Dist: pre-commit; extra == 'dev'
30
31
  Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
31
32
  Requires-Dist: pytest>=8.0; extra == 'dev'
33
+ Provides-Extra: mcp
34
+ Requires-Dist: fastmcp<5,>=3.0; extra == 'mcp'
32
35
  Description-Content-Type: text/markdown
33
36
 
34
37
  # patchright-cli
@@ -149,7 +152,7 @@ graph LR
149
152
  |-----------|------|
150
153
  | **Daemon** (`daemon.py`) | Long-running process managing browser sessions via Patchright. Auto-starts on first `open`. |
151
154
  | **CLI** (`cli.py`) | Thin client — connects, sends command, prints result, disconnects. Browser stays open. |
152
- | **Snapshot** (`snapshot.py`) | `TreeWalker`-based DOM scan assigning `data-patchright-ref` attributes for element targeting. |
155
+ | **Snapshot** (`snapshot.py`) | Playwright `aria_snapshot()` accessibility tree, annotated with sequential `[ref=eN]` handles for element targeting. |
153
156
 
154
157
  ---
155
158
 
@@ -159,9 +162,12 @@ graph LR
159
162
  ### Core
160
163
  ```bash
161
164
  patchright-cli open [url] # Launch browser
162
- patchright-cli open --persistent # With persistent profile
165
+ patchright-cli open --profile=work # Named profile any session can use (logins persist)
163
166
  patchright-cli open --headless # Run headless
164
- patchright-cli open --profile=<path> # Custom profile directory
167
+ patchright-cli open --profile=./dir # A value with / or \ is a profile directory
168
+ patchright-cli --tab <name> open <url> # Named tab: shared browser, own page/refs
169
+ patchright-cli open --mobile # Emulate a generic mobile device (Pixel 7)
170
+ patchright-cli open --device="Pixel 5" # Emulate a specific Playwright device
165
171
  patchright-cli goto <url> # Navigate
166
172
  patchright-cli click <ref> # Click element
167
173
  patchright-cli click <ref> right # Right-click
@@ -180,8 +186,13 @@ patchright-cli drag <from> <to> # Drag and drop
180
186
  patchright-cli snapshot # Accessibility snapshot
181
187
  patchright-cli snapshot -i # Interactive elements only (recommended)
182
188
  patchright-cli snapshot --depth=3 # Limit depth
189
+ patchright-cli snapshot --selector=CSS # Snapshot a CSS-scoped subtree
183
190
  patchright-cli snapshot <ref> # Snapshot element subtree
184
191
  patchright-cli snapshot --filename=f # Save to custom path
192
+ patchright-cli find "text" # Search snapshot for text
193
+ patchright-cli find --regex "a|b" # Search by regex
194
+ patchright-cli find "text" --all # Include text/paragraph nodes
195
+ patchright-cli find "text" --limit=50 # Cap hits (default 20)
185
196
  patchright-cli eval <expr> # Run JavaScript
186
197
  patchright-cli eval --file=script.js # Run JS from file
187
198
  patchright-cli run-code <code> # Run JS with return value
@@ -190,6 +201,7 @@ patchright-cli screenshot # Page screenshot
190
201
  patchright-cli screenshot --full-page # Full scrollable page
191
202
  patchright-cli screenshot <ref> # Element screenshot
192
203
  patchright-cli screenshot --filename=f # Save to custom path
204
+ patchright-cli screenshot --hires # Capture at device pixel ratio
193
205
  patchright-cli close # Close session
194
206
  ```
195
207
 
@@ -198,6 +210,10 @@ patchright-cli close # Close session
198
210
  patchright-cli go-back
199
211
  patchright-cli go-forward
200
212
  patchright-cli reload
213
+ patchright-cli wait 500 # Wait a fixed number of milliseconds
214
+ patchright-cli wait --url="**/dashboard" # Wait for the URL to match (`*` never crosses a `/`)
215
+ patchright-cli wait-for <ref> # Wait until an element is visible
216
+ patchright-cli wait-for <ref> --state=hidden
201
217
  ```
202
218
 
203
219
  ### Keyboard / Mouse
@@ -285,6 +301,9 @@ patchright-cli tracing-stop # Saves .zip trace file
285
301
  patchright-cli video-start # Start video recording (CDP screencast)
286
302
  patchright-cli video-stop # Stop and save video (requires ffmpeg for .webm)
287
303
  patchright-cli video-stop --filename=recording.webm
304
+ patchright-cli show --annotate # Ask a human to mark up the page, wait for it
305
+ patchright-cli video-show-actions # Annotate each action in the recording
306
+ patchright-cli video-hide-actions # Stop annotating actions
288
307
  patchright-cli pdf --filename=page.pdf
289
308
  ```
290
309
 
@@ -317,13 +336,15 @@ patchright-cli install --skills # Auto-install skills for detected AI age
317
336
 
318
337
  ### Sessions
319
338
  ```bash
320
- patchright-cli -s=mysession open https://example.com --persistent
339
+ patchright-cli -s=mysession open https://example.com
321
340
  patchright-cli -s=mysession click e6
322
341
  patchright-cli -s=mysession close
323
342
  patchright-cli list # List all sessions
324
343
  patchright-cli close-all
325
344
  patchright-cli kill-all
326
- patchright-cli delete-data # Delete persistent profile
345
+ patchright-cli delete-data # Delete the profile named after the session
346
+ patchright-cli profile-list # Profiles, size, and which session uses each
347
+ patchright-cli profile-delete <name> # Delete an idle named profile
327
348
  patchright-cli --port=9322 open # Custom daemon port
328
349
  ```
329
350
 
@@ -354,6 +375,34 @@ patchright-cli install --skills
354
375
 
355
376
  </details>
356
377
 
378
+ ### MCP server
379
+
380
+ MCP support is optional so the base CLI install stays small:
381
+
382
+ ```bash
383
+ pip install 'patchright-cli[mcp]'
384
+
385
+ # stdio (default)
386
+ patchright-cli mcp
387
+
388
+ # Streamable HTTP
389
+ patchright-cli mcp --http --host=127.0.0.1 --port=8000
390
+ ```
391
+
392
+ ![How a remote MCP call reaches Chrome](docs/mcp-architecture.svg)
393
+
394
+ The HTTP endpoint is `/mcp`. Set `PATCHRIGHT_CLI_MCP_TOKEN` to require `Authorization: Bearer <token>` on every request, and always set it before binding to `0.0.0.0`.
395
+
396
+ File paths passed to MCP tools (uploads, state and cookie files, screenshot/PDF/snapshot output, profile directories) must be absolute and beneath the MCP root, so a client cannot reach arbitrary host files. Outputs without a path land in `<root>/output/`, which `browser_delete_output` can clean up. `patchright_eval` and `patchright_run_code` still run arbitrary JavaScript in the page, so protect HTTP deployments regardless.
397
+
398
+ | Variable | Default | Effect |
399
+ |---|---|---|
400
+ | `PATCHRIGHT_CLI_MCP_TOKEN` | unset (no auth) | Bearer token required by the HTTP transport |
401
+ | `PATCHRIGHT_CLI_MCP_ROOT` | working directory | Root that tool file paths must sit beneath |
402
+ | `PATCHRIGHT_CLI_MCP_UNRESTRICTED_FILESYSTEM` | unset | `1` drops the path check and enables `patchright_install_skills` |
403
+ | `PATCHRIGHT_CLI_MCP_HEADLESS` | unset (headed) | `1` makes `patchright_open` default to headless, for hosts without a display |
404
+ | `PATCHRIGHT_CLI_MCP_DAEMON_PORT` | `9321` | Daemon port the tools talk to |
405
+
357
406
  ### Skill docs
358
407
 
359
408
  | File | Topic |
@@ -116,7 +116,7 @@ graph LR
116
116
  |-----------|------|
117
117
  | **Daemon** (`daemon.py`) | Long-running process managing browser sessions via Patchright. Auto-starts on first `open`. |
118
118
  | **CLI** (`cli.py`) | Thin client — connects, sends command, prints result, disconnects. Browser stays open. |
119
- | **Snapshot** (`snapshot.py`) | `TreeWalker`-based DOM scan assigning `data-patchright-ref` attributes for element targeting. |
119
+ | **Snapshot** (`snapshot.py`) | Playwright `aria_snapshot()` accessibility tree, annotated with sequential `[ref=eN]` handles for element targeting. |
120
120
 
121
121
  ---
122
122
 
@@ -126,9 +126,12 @@ graph LR
126
126
  ### Core
127
127
  ```bash
128
128
  patchright-cli open [url] # Launch browser
129
- patchright-cli open --persistent # With persistent profile
129
+ patchright-cli open --profile=work # Named profile any session can use (logins persist)
130
130
  patchright-cli open --headless # Run headless
131
- patchright-cli open --profile=<path> # Custom profile directory
131
+ patchright-cli open --profile=./dir # A value with / or \ is a profile directory
132
+ patchright-cli --tab <name> open <url> # Named tab: shared browser, own page/refs
133
+ patchright-cli open --mobile # Emulate a generic mobile device (Pixel 7)
134
+ patchright-cli open --device="Pixel 5" # Emulate a specific Playwright device
132
135
  patchright-cli goto <url> # Navigate
133
136
  patchright-cli click <ref> # Click element
134
137
  patchright-cli click <ref> right # Right-click
@@ -147,8 +150,13 @@ patchright-cli drag <from> <to> # Drag and drop
147
150
  patchright-cli snapshot # Accessibility snapshot
148
151
  patchright-cli snapshot -i # Interactive elements only (recommended)
149
152
  patchright-cli snapshot --depth=3 # Limit depth
153
+ patchright-cli snapshot --selector=CSS # Snapshot a CSS-scoped subtree
150
154
  patchright-cli snapshot <ref> # Snapshot element subtree
151
155
  patchright-cli snapshot --filename=f # Save to custom path
156
+ patchright-cli find "text" # Search snapshot for text
157
+ patchright-cli find --regex "a|b" # Search by regex
158
+ patchright-cli find "text" --all # Include text/paragraph nodes
159
+ patchright-cli find "text" --limit=50 # Cap hits (default 20)
152
160
  patchright-cli eval <expr> # Run JavaScript
153
161
  patchright-cli eval --file=script.js # Run JS from file
154
162
  patchright-cli run-code <code> # Run JS with return value
@@ -157,6 +165,7 @@ patchright-cli screenshot # Page screenshot
157
165
  patchright-cli screenshot --full-page # Full scrollable page
158
166
  patchright-cli screenshot <ref> # Element screenshot
159
167
  patchright-cli screenshot --filename=f # Save to custom path
168
+ patchright-cli screenshot --hires # Capture at device pixel ratio
160
169
  patchright-cli close # Close session
161
170
  ```
162
171
 
@@ -165,6 +174,10 @@ patchright-cli close # Close session
165
174
  patchright-cli go-back
166
175
  patchright-cli go-forward
167
176
  patchright-cli reload
177
+ patchright-cli wait 500 # Wait a fixed number of milliseconds
178
+ patchright-cli wait --url="**/dashboard" # Wait for the URL to match (`*` never crosses a `/`)
179
+ patchright-cli wait-for <ref> # Wait until an element is visible
180
+ patchright-cli wait-for <ref> --state=hidden
168
181
  ```
169
182
 
170
183
  ### Keyboard / Mouse
@@ -252,6 +265,9 @@ patchright-cli tracing-stop # Saves .zip trace file
252
265
  patchright-cli video-start # Start video recording (CDP screencast)
253
266
  patchright-cli video-stop # Stop and save video (requires ffmpeg for .webm)
254
267
  patchright-cli video-stop --filename=recording.webm
268
+ patchright-cli show --annotate # Ask a human to mark up the page, wait for it
269
+ patchright-cli video-show-actions # Annotate each action in the recording
270
+ patchright-cli video-hide-actions # Stop annotating actions
255
271
  patchright-cli pdf --filename=page.pdf
256
272
  ```
257
273
 
@@ -284,13 +300,15 @@ patchright-cli install --skills # Auto-install skills for detected AI age
284
300
 
285
301
  ### Sessions
286
302
  ```bash
287
- patchright-cli -s=mysession open https://example.com --persistent
303
+ patchright-cli -s=mysession open https://example.com
288
304
  patchright-cli -s=mysession click e6
289
305
  patchright-cli -s=mysession close
290
306
  patchright-cli list # List all sessions
291
307
  patchright-cli close-all
292
308
  patchright-cli kill-all
293
- patchright-cli delete-data # Delete persistent profile
309
+ patchright-cli delete-data # Delete the profile named after the session
310
+ patchright-cli profile-list # Profiles, size, and which session uses each
311
+ patchright-cli profile-delete <name> # Delete an idle named profile
294
312
  patchright-cli --port=9322 open # Custom daemon port
295
313
  ```
296
314
 
@@ -321,6 +339,34 @@ patchright-cli install --skills
321
339
 
322
340
  </details>
323
341
 
342
+ ### MCP server
343
+
344
+ MCP support is optional so the base CLI install stays small:
345
+
346
+ ```bash
347
+ pip install 'patchright-cli[mcp]'
348
+
349
+ # stdio (default)
350
+ patchright-cli mcp
351
+
352
+ # Streamable HTTP
353
+ patchright-cli mcp --http --host=127.0.0.1 --port=8000
354
+ ```
355
+
356
+ ![How a remote MCP call reaches Chrome](docs/mcp-architecture.svg)
357
+
358
+ The HTTP endpoint is `/mcp`. Set `PATCHRIGHT_CLI_MCP_TOKEN` to require `Authorization: Bearer <token>` on every request, and always set it before binding to `0.0.0.0`.
359
+
360
+ File paths passed to MCP tools (uploads, state and cookie files, screenshot/PDF/snapshot output, profile directories) must be absolute and beneath the MCP root, so a client cannot reach arbitrary host files. Outputs without a path land in `<root>/output/`, which `browser_delete_output` can clean up. `patchright_eval` and `patchright_run_code` still run arbitrary JavaScript in the page, so protect HTTP deployments regardless.
361
+
362
+ | Variable | Default | Effect |
363
+ |---|---|---|
364
+ | `PATCHRIGHT_CLI_MCP_TOKEN` | unset (no auth) | Bearer token required by the HTTP transport |
365
+ | `PATCHRIGHT_CLI_MCP_ROOT` | working directory | Root that tool file paths must sit beneath |
366
+ | `PATCHRIGHT_CLI_MCP_UNRESTRICTED_FILESYSTEM` | unset | `1` drops the path check and enables `patchright_install_skills` |
367
+ | `PATCHRIGHT_CLI_MCP_HEADLESS` | unset (headed) | `1` makes `patchright_open` default to headless, for hosts without a display |
368
+ | `PATCHRIGHT_CLI_MCP_DAEMON_PORT` | `9321` | Daemon port the tools talk to |
369
+
324
370
  ### Skill docs
325
371
 
326
372
  | File | Topic |
@@ -0,0 +1,120 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1200" height="470" viewBox="0 0 1200 470" role="img" aria-label="A remote MCP client sends tools/call over HTTP across a network boundary guarded by an optional Bearer token to the patchright-cli MCP server on the host. The server spawns one short-lived CLI process per call, which sends length-prefixed JSON over TCP to the long-lived daemon. The daemon drives real Chrome over CDP; headed Chrome draws on an Xvfb display and loads websites over HTTPS. Output files land in the root's output directory on the host.">
2
+ <style>
3
+ svg { --bg: #ffffff; --fg: #1b2230; --muted: #5d6778; --line: #c9cfd9; --accent: #c2410c; --host: #eef1f6;
4
+ color: var(--fg); font-family: -apple-system, "Segoe UI", Helvetica, Arial, sans-serif; }
5
+ @media (prefers-color-scheme: dark) {
6
+ svg { --bg: #0d1117; --fg: #e6edf3; --muted: #9aa4b4; --line: #3a4352; --accent: #fb923c; --host: #161b22; }
7
+ }
8
+ .bg { fill: var(--bg); }
9
+ .box { fill: var(--bg); stroke: currentColor; stroke-width: 1.4; }
10
+ .ghost { fill: var(--bg); stroke: var(--line); stroke-width: 1.2; }
11
+ .hostbg { fill: var(--host); stroke: var(--line); stroke-width: 1.2; }
12
+ .t { fill: currentColor; font-size: 14px; font-weight: 600; text-anchor: middle; }
13
+ .s { fill: var(--muted); font-size: 11.5px; text-anchor: middle; }
14
+ .m { fill: var(--muted); font-family: ui-monospace, Consolas, monospace; font-size: 11px; text-anchor: middle; }
15
+ .lbl { fill: currentColor; font-size: 12px; text-anchor: middle; paint-order: stroke; stroke: var(--bg); stroke-width: 5px; stroke-linejoin: round; }
16
+ .lbl.start { text-anchor: start; }
17
+ .region { fill: var(--muted); font-family: ui-monospace, Consolas, monospace; font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; }
18
+ .edge { stroke: currentColor; stroke-width: 1.4; fill: none; }
19
+ .edge.dim { stroke: var(--muted); stroke-dasharray: 4 3; }
20
+ .boundary { stroke: var(--accent); stroke-width: 1.6; stroke-dasharray: 6 5; }
21
+ .warn { fill: var(--accent); font-size: 12px; font-weight: 600; text-anchor: middle; paint-order: stroke; stroke: var(--bg); stroke-width: 5px; }
22
+ .head { fill: currentColor; }
23
+ .head.dim { fill: var(--muted); }
24
+ </style>
25
+ <rect class="bg" x="0" y="0" width="1200" height="470"/>
26
+ <defs>
27
+ <marker id="ah" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse">
28
+ <path class="head" d="M0,0 L10,5 L0,10 z"/>
29
+ </marker>
30
+ <marker id="ahd" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse">
31
+ <path class="head dim" d="M0,0 L10,5 L0,10 z"/>
32
+ </marker>
33
+ </defs>
34
+
35
+ <!-- host region -->
36
+ <rect class="hostbg" x="250" y="30" width="820" height="420" rx="10"/>
37
+ <text class="region" x="268" y="54">Host · container or server</text>
38
+ <text class="region" x="20" y="54">Remote machine</text>
39
+ <text class="region" x="1100" y="54">Internet</text>
40
+
41
+ <!-- trust boundary -->
42
+ <line class="boundary" x1="205" y1="24" x2="205" y2="456"/>
43
+ <text class="warn" x="205" y="300">trust boundary</text>
44
+ <text class="warn" x="205" y="317" style="font-weight:400">optional Bearer token</text>
45
+
46
+ <!-- client -->
47
+ <rect class="box" x="20" y="170" width="140" height="80" rx="6"/>
48
+ <text class="t" x="90" y="205">MCP client</text>
49
+ <text class="s" x="90" y="225">Claude Code, agents</text>
50
+
51
+ <!-- client <-> server -->
52
+ <line class="edge" x1="160" y1="195" x2="268" y2="195" marker-end="url(#ah)"/>
53
+ <text class="lbl" x="215" y="187">tools/call</text>
54
+ <line class="edge" x1="270" y1="228" x2="162" y2="228" marker-end="url(#ah)"/>
55
+ <text class="lbl" x="215" y="246">text · PNG · YAML</text>
56
+
57
+ <!-- MCP server -->
58
+ <rect class="box" x="270" y="170" width="170" height="80" rx="6"/>
59
+ <text class="t" x="355" y="198">MCP server</text>
60
+ <text class="m" x="355" y="217">mcp --http :8000/mcp</text>
61
+ <text class="s" x="355" y="236">checks session, paths</text>
62
+
63
+ <!-- server -> CLI -->
64
+ <line class="edge" x1="440" y1="210" x2="518" y2="210" marker-end="url(#ah)"/>
65
+ <text class="lbl" x="479" y="202">spawns</text>
66
+
67
+ <!-- CLI process stack -->
68
+ <rect class="ghost" x="536" y="158" width="160" height="80" rx="6"/>
69
+ <rect class="ghost" x="528" y="164" width="160" height="80" rx="6"/>
70
+ <rect class="box" x="520" y="170" width="160" height="80" rx="6"/>
71
+ <text class="t" x="600" y="198">CLI process</text>
72
+ <text class="m" x="600" y="217">python -m patchright_cli</text>
73
+ <text class="s" x="600" y="236">one per call, exits</text>
74
+
75
+ <!-- CLI -> daemon -->
76
+ <line class="edge" x1="696" y1="210" x2="778" y2="210" marker-end="url(#ah)"/>
77
+ <text class="lbl" x="737" y="202">TCP · JSON</text>
78
+ <text class="s" x="737" y="229">starts it</text>
79
+ <text class="s" x="737" y="243">if absent</text>
80
+
81
+ <!-- daemon -->
82
+ <rect class="box" x="780" y="170" width="120" height="80" rx="6"/>
83
+ <text class="t" x="840" y="198">Daemon</text>
84
+ <text class="m" x="840" y="217">127.0.0.1:9321</text>
85
+ <text class="s" x="840" y="236">long-lived</text>
86
+
87
+ <!-- daemon -> chrome -->
88
+ <line class="edge" x1="900" y1="210" x2="948" y2="210" marker-end="url(#ah)"/>
89
+ <text class="lbl" x="924" y="202">CDP</text>
90
+
91
+ <!-- chrome -->
92
+ <rect class="box" x="950" y="170" width="100" height="80" rx="6"/>
93
+ <text class="t" x="1000" y="205">Chrome</text>
94
+ <text class="s" x="1000" y="225">headed or headless</text>
95
+
96
+ <!-- xvfb -->
97
+ <rect class="ghost" x="950" y="72" width="100" height="48" rx="6"/>
98
+ <text class="t" x="1000" y="96" style="font-weight:500">Xvfb :99</text>
99
+ <text class="s" x="1000" y="112">headed only</text>
100
+ <line class="edge dim" x1="1000" y1="120" x2="1000" y2="168" marker-end="url(#ahd)"/>
101
+ <text class="lbl start" x="1008" y="149" style="fill:var(--muted)">DISPLAY</text>
102
+
103
+ <!-- chrome -> web -->
104
+ <line class="edge" x1="1050" y1="210" x2="1098" y2="210" marker-end="url(#ah)"/>
105
+ <text class="lbl" x="1075" y="202">HTTPS</text>
106
+ <rect class="box" x="1100" y="170" width="80" height="80" rx="6"/>
107
+ <text class="t" x="1140" y="214">Websites</text>
108
+
109
+ <!-- output dir -->
110
+ <rect class="box" x="520" y="350" width="380" height="60" rx="6"/>
111
+ <text class="m" x="710" y="375" style="fill:currentColor;font-size:12px">&lt;root&gt;/output/</text>
112
+ <text class="s" x="710" y="395">screenshots · PDFs · snapshots · state files</text>
113
+
114
+ <line class="edge" x1="840" y1="250" x2="840" y2="348" marker-end="url(#ah)"/>
115
+ <text class="lbl start" x="848" y="304">writes files</text>
116
+ <line class="edge dim" x1="600" y1="252" x2="600" y2="348" marker-end="url(#ahd)"/>
117
+ <text class="lbl start" x="608" y="304" style="fill:var(--muted)">cwd</text>
118
+ <polyline class="edge dim" points="355,250 355,380 518,380" marker-end="url(#ahd)"/>
119
+ <text class="lbl" x="437" y="372" style="fill:var(--muted)">delete_output</text>
120
+ </svg>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "patchright-cli"
3
- version = "0.6.0"
3
+ version = "0.8.0"
4
4
  description = "Anti-detect browser automation CLI using Patchright (undetected Playwright fork)"
5
5
  license = "Apache-2.0"
6
6
  requires-python = ">=3.10"
@@ -54,16 +54,21 @@ markers = [
54
54
  ]
55
55
 
56
56
  [project.optional-dependencies]
57
+ mcp = [
58
+ "fastmcp>=3.0,<5",
59
+ ]
57
60
  dev = [
58
61
  "pytest>=8.0",
59
62
  "pytest-asyncio>=0.23",
60
63
  "pre-commit",
64
+ "fastmcp>=3.0,<5",
61
65
  ]
62
66
 
63
67
  [dependency-groups]
64
68
  dev = [
65
69
  "pytest>=8.0",
66
70
  "pytest-asyncio>=0.23",
71
+ "fastmcp>=3.0,<5",
67
72
  ]
68
73
 
69
74
  [tool.ruff]