patchright-cli 0.7.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.
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/.claude-plugin/marketplace.json +1 -1
- patchright_cli-0.7.0/README.md → patchright_cli-0.8.0/PKG-INFO +64 -0
- patchright_cli-0.7.0/PKG-INFO → patchright_cli-0.8.0/README.md +28 -33
- patchright_cli-0.8.0/docs/mcp-architecture.svg +120 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/pyproject.toml +6 -1
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/src/patchright_cli/__init__.py +1 -1
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/src/patchright_cli/cli.py +44 -1
- patchright_cli-0.8.0/src/patchright_cli/mcp_server.py +1325 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/tests/test_daemon_handlers.py +32 -0
- patchright_cli-0.8.0/tests/test_mcp.py +172 -0
- patchright_cli-0.8.0/uv.lock +2953 -0
- patchright_cli-0.7.0/uv.lock +0 -1154
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/.claude-plugin/skills/browser-automation.json +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/.github/workflows/ci.yml +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/.github/workflows/publish.yml +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/.gitignore +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/.pre-commit-config.yaml +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/CLAUDE.md +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/CONTRIBUTING.md +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/GLOSSARY.md +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/LICENSE +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/Makefile +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/docs/installation.md +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/patchright-cli +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/patchright-cli.cmd +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/skills/patchright-cli/SKILL.md +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/skills/patchright-cli/references/request-mocking.md +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/skills/patchright-cli/references/running-code.md +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/skills/patchright-cli/references/session-management.md +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/skills/patchright-cli/references/snapshot-refs.md +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/skills/patchright-cli/references/storage-state.md +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/skills/patchright-cli/references/tracing.md +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/skills/patchright-cli/references/video-recording.md +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/src/patchright_cli/__main__.py +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/src/patchright_cli/daemon.py +24 -24
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/src/patchright_cli/dashboard.py +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/src/patchright_cli/ref_registry.py +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/src/patchright_cli/snapshot.py +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/tests/__init__.py +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/tests/fixture.html +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/tests/test_annotate.py +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/tests/test_attach.py +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/tests/test_cli_config.py +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/tests/test_e2e.py +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/tests/test_packaging.py +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/tests/test_profiles.py +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/tests/test_ref_registry.py +0 -0
- {patchright_cli-0.7.0 → patchright_cli-0.8.0}/tests/test_tabs.py +0 -0
|
@@ -1,3 +1,39 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: patchright-cli
|
|
3
|
+
Version: 0.8.0
|
|
4
|
+
Summary: Anti-detect browser automation CLI using Patchright (undetected Playwright fork)
|
|
5
|
+
Project-URL: Homepage, https://github.com/AhaiMk01/patchright-cli
|
|
6
|
+
Project-URL: Repository, https://github.com/AhaiMk01/patchright-cli
|
|
7
|
+
Project-URL: Issues, https://github.com/AhaiMk01/patchright-cli/issues
|
|
8
|
+
Author: zpahai
|
|
9
|
+
License-Expression: Apache-2.0
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: anti-detect,automation,browser,cli,patchright,playwright,scraping
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
|
|
22
|
+
Classifier: Topic :: Software Development :: Testing
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: aiohttp>=3.9
|
|
25
|
+
Requires-Dist: click
|
|
26
|
+
Requires-Dist: patchright
|
|
27
|
+
Requires-Dist: pyyaml
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: fastmcp<5,>=3.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: pre-commit; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
33
|
+
Provides-Extra: mcp
|
|
34
|
+
Requires-Dist: fastmcp<5,>=3.0; extra == 'mcp'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
1
37
|
# patchright-cli
|
|
2
38
|
|
|
3
39
|
[](https://pypi.org/project/patchright-cli/)
|
|
@@ -339,6 +375,34 @@ patchright-cli install --skills
|
|
|
339
375
|
|
|
340
376
|
</details>
|
|
341
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
|
+

|
|
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
|
+
|
|
342
406
|
### Skill docs
|
|
343
407
|
|
|
344
408
|
| File | Topic |
|
|
@@ -1,36 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.5
|
|
2
|
-
Name: patchright-cli
|
|
3
|
-
Version: 0.7.0
|
|
4
|
-
Summary: Anti-detect browser automation CLI using Patchright (undetected Playwright fork)
|
|
5
|
-
Project-URL: Homepage, https://github.com/AhaiMk01/patchright-cli
|
|
6
|
-
Project-URL: Repository, https://github.com/AhaiMk01/patchright-cli
|
|
7
|
-
Project-URL: Issues, https://github.com/AhaiMk01/patchright-cli/issues
|
|
8
|
-
Author: zpahai
|
|
9
|
-
License-Expression: Apache-2.0
|
|
10
|
-
License-File: LICENSE
|
|
11
|
-
Keywords: anti-detect,automation,browser,cli,patchright,playwright,scraping
|
|
12
|
-
Classifier: Development Status :: 4 - Beta
|
|
13
|
-
Classifier: Environment :: Console
|
|
14
|
-
Classifier: Intended Audience :: Developers
|
|
15
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
16
|
-
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
-
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
|
|
22
|
-
Classifier: Topic :: Software Development :: Testing
|
|
23
|
-
Requires-Python: >=3.10
|
|
24
|
-
Requires-Dist: aiohttp>=3.9
|
|
25
|
-
Requires-Dist: click
|
|
26
|
-
Requires-Dist: patchright
|
|
27
|
-
Requires-Dist: pyyaml
|
|
28
|
-
Provides-Extra: dev
|
|
29
|
-
Requires-Dist: pre-commit; extra == 'dev'
|
|
30
|
-
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
31
|
-
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
32
|
-
Description-Content-Type: text/markdown
|
|
33
|
-
|
|
34
1
|
# patchright-cli
|
|
35
2
|
|
|
36
3
|
[](https://pypi.org/project/patchright-cli/)
|
|
@@ -372,6 +339,34 @@ patchright-cli install --skills
|
|
|
372
339
|
|
|
373
340
|
</details>
|
|
374
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
|
+

|
|
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
|
+
|
|
375
370
|
### Skill docs
|
|
376
371
|
|
|
377
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"><root>/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.
|
|
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]
|
|
@@ -6,6 +6,7 @@ receives the result, and prints it.
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
+
import argparse
|
|
9
10
|
import json
|
|
10
11
|
import os
|
|
11
12
|
import socket
|
|
@@ -248,6 +249,36 @@ def _handle_install(args: list) -> None:
|
|
|
248
249
|
click.echo(f"\npatchright-cli v{__version__} skills installed.")
|
|
249
250
|
|
|
250
251
|
|
|
252
|
+
def _handle_mcp(args: list[str]) -> None:
|
|
253
|
+
"""Handle the optional MCP server command."""
|
|
254
|
+
parser = argparse.ArgumentParser(
|
|
255
|
+
prog="patchright-cli mcp",
|
|
256
|
+
description="Run the MCP server. Stdio is the default transport.",
|
|
257
|
+
)
|
|
258
|
+
parser.add_argument("--http", action="store_true", help="Use Streamable HTTP instead of stdio.")
|
|
259
|
+
parser.add_argument("--host", default="127.0.0.1", help="HTTP bind host.")
|
|
260
|
+
parser.add_argument("--port", type=int, default=8000, help="HTTP bind port.")
|
|
261
|
+
options = parser.parse_args(args)
|
|
262
|
+
|
|
263
|
+
if not 1 <= options.port <= 65535:
|
|
264
|
+
parser.error("--port must be between 1 and 65535")
|
|
265
|
+
if not options.http and (options.host != "127.0.0.1" or options.port != 8000):
|
|
266
|
+
parser.error("--host and --port require --http")
|
|
267
|
+
|
|
268
|
+
try:
|
|
269
|
+
from patchright_cli.mcp_server import run_mcp_server
|
|
270
|
+
except ImportError as exc:
|
|
271
|
+
if exc.name not in {"fastmcp", "mcp", "pydantic"}:
|
|
272
|
+
raise
|
|
273
|
+
click.echo(
|
|
274
|
+
"MCP support is not installed. Install it with: pip install 'patchright-cli[mcp]'",
|
|
275
|
+
err=True,
|
|
276
|
+
)
|
|
277
|
+
raise SystemExit(1) from exc
|
|
278
|
+
|
|
279
|
+
run_mcp_server(http=options.http, host=options.host, port=options.port)
|
|
280
|
+
|
|
281
|
+
|
|
251
282
|
_VERSION_CHECK_INTERVAL = 24 * 60 * 60
|
|
252
283
|
_PYPI_URL = "https://pypi.org/pypi/patchright-cli/json"
|
|
253
284
|
|
|
@@ -510,6 +541,7 @@ COMMANDS_HELP = {
|
|
|
510
541
|
"codegen-stop": "codegen-stop [file] Stop recording and save script",
|
|
511
542
|
# Setup
|
|
512
543
|
"install": "install --skills Install skill files for AI agents",
|
|
544
|
+
"mcp": "mcp [--http] [--host=HOST] [--port=N] Start MCP server (stdio by default)",
|
|
513
545
|
}
|
|
514
546
|
|
|
515
547
|
ALL_COMMANDS = list(COMMANDS_HELP.keys())
|
|
@@ -616,7 +648,7 @@ def _print_help():
|
|
|
616
648
|
("Profiles", ["profile-list", "profile-delete"]),
|
|
617
649
|
("Dashboard", ["show"]),
|
|
618
650
|
("Codegen", ["codegen", "codegen-stop"]),
|
|
619
|
-
("Setup", ["install"]),
|
|
651
|
+
("Setup", ["install", "mcp"]),
|
|
620
652
|
]
|
|
621
653
|
for cat_name, cmds in categories:
|
|
622
654
|
click.echo(f"\n {cat_name}:")
|
|
@@ -630,6 +662,13 @@ def main():
|
|
|
630
662
|
_soften_output_encoding(sys.stdout, sys.stderr)
|
|
631
663
|
argv = sys.argv[1:]
|
|
632
664
|
|
|
665
|
+
# MCP is a self-contained subcommand with its own transport options.
|
|
666
|
+
# Dispatch it before global option parsing so `patchright-cli mcp --help`
|
|
667
|
+
# shows MCP help rather than the top-level CLI help.
|
|
668
|
+
if argv and argv[0] == "mcp":
|
|
669
|
+
_handle_mcp(argv[1:])
|
|
670
|
+
return
|
|
671
|
+
|
|
633
672
|
# Parse global options manually (before the command)
|
|
634
673
|
headless = False
|
|
635
674
|
persistent = False
|
|
@@ -779,6 +818,10 @@ def main():
|
|
|
779
818
|
_handle_install(args + [k if v is True else f"{k}={v}" for k, v in extra_opts.items()])
|
|
780
819
|
sys.exit(0)
|
|
781
820
|
|
|
821
|
+
if command == "mcp":
|
|
822
|
+
_handle_mcp(args)
|
|
823
|
+
sys.exit(0)
|
|
824
|
+
|
|
782
825
|
_warn_stale_skills()
|
|
783
826
|
|
|
784
827
|
# Only on `open`: one browser launch already costs seconds, and this keeps
|