mcp-camoufox 0.5.2 → 0.5.3
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 +35 -6
- package/dist/index.js +61 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
</div>
|
|
13
13
|
|
|
14
|
-
The most feature-rich stealth browser MCP server. **
|
|
14
|
+
The most feature-rich stealth browser MCP server. **80 tools** for full browser control powered by [Camoufox](https://github.com/daijro/camoufox) — a Firefox fork with C++ level anti-detection that bypasses Cloudflare, bot detection, and anti-automation.
|
|
15
15
|
|
|
16
16
|
> **One command. No Python. No manual setup. Everything auto-installs.**
|
|
17
17
|
|
|
@@ -39,7 +39,35 @@ claude mcp add camoufox -- npx -y mcp-camoufox@latest
|
|
|
39
39
|
| redf0x1/camofox-mcp | 45 | Yes | No (clone) | Yes |
|
|
40
40
|
| Sekinal/camoufox-mcp | 49 | Yes | No (clone) | Yes |
|
|
41
41
|
| Playwright CLI | 60+ | No | Yes | Yes |
|
|
42
|
-
| **[mcp-camoufox](https://github.com/RobithYusuf/mcp-camoufox)** | **
|
|
42
|
+
| **[mcp-camoufox](https://github.com/RobithYusuf/mcp-camoufox)** | **80** | **Yes** | **Yes** | **Yes** |
|
|
43
|
+
|
|
44
|
+
## Proven on Real Sites
|
|
45
|
+
|
|
46
|
+
| Site | Challenge | Result |
|
|
47
|
+
|------|-----------|--------|
|
|
48
|
+
| `2captcha.com/demo/cloudflare-turnstile` | Cloudflare Turnstile widget | ✅ **"Success!"** via `click_turnstile()` tool ([proof](docs/images/turnstile.jpg)) |
|
|
49
|
+
| `bot.sannysoft.com` | Firefox fingerprint tests | ✅ All green ([proof](docs/images/sannysoft.jpg)) |
|
|
50
|
+
| `browserscan.net/bot-detection` | WebDriver/UA/CDP/Navigator | ✅ All categories "Normal" ([proof](docs/images/browserscan.jpg)) |
|
|
51
|
+
|
|
52
|
+
### 🎯 Cloudflare Turnstile → Success via `click_turnstile()`
|
|
53
|
+
|
|
54
|
+
<img src="docs/images/turnstile.jpg" alt="Cloudflare Turnstile success" width="500">
|
|
55
|
+
|
|
56
|
+
`click_turnstile()` auto-detects the widget via 6 selector fallback (`iframe[src*=challenges.cloudflare.com]`, `[data-sitekey]`, `.cf-turnstile`, …), computes checkbox position (offset_x=30 from widget left), and clicks with a 3-step Bezier-like approach — combined with Camoufox's native `humanize` + `disable_coop` for cross-origin iframe click.
|
|
57
|
+
|
|
58
|
+
**Scope:** works on **Interactive Turnstile** (visible iframe widget). **Managed Challenge** interstitials ("Just a moment...") render the widget in shadow DOM — not supported here; use sister project [mcp-stealth-chrome](https://github.com/RobithYusuf/mcp-stealth-chrome) (Chrome+CDP) for those. Real-world bypass success also depends on IP reputation and browser fingerprint — code alone doesn't guarantee it.
|
|
59
|
+
|
|
60
|
+
### 🧪 bot.sannysoft.com → Firefox Fingerprint Pass
|
|
61
|
+
|
|
62
|
+
<img src="docs/images/sannysoft.jpg" alt="sannysoft Firefox pass" width="500">
|
|
63
|
+
|
|
64
|
+
User Agent reports `Firefox/135.0`, WebDriver missing, WebDriver Advanced passed, Permissions prompt, Plugins length 5 passed, Languages `en-US,en`, WebGL Intel HD Graphics — all green. ("Chrome: missing" is expected — Camoufox spoofs Firefox, not Chrome.)
|
|
65
|
+
|
|
66
|
+
### 🔍 browserscan.net/bot-detection → All Categories Normal
|
|
67
|
+
|
|
68
|
+
<img src="docs/images/browserscan.jpg" alt="browserscan normal" width="500">
|
|
69
|
+
|
|
70
|
+
WebDriver, User-Agent, CDP, Navigator — every detection category returns **"Normal"**. Camoufox's C++-level Firefox patches leave zero automation signals.
|
|
43
71
|
|
|
44
72
|
## Setup
|
|
45
73
|
|
|
@@ -234,7 +262,7 @@ Or via UI: Agent Panel > `...` > MCP Servers > Manage MCP Servers > View raw con
|
|
|
234
262
|
|
|
235
263
|
That's all. Camoufox browser binary (~80MB) downloads automatically on first launch.
|
|
236
264
|
|
|
237
|
-
## All
|
|
265
|
+
## All 80 Tools
|
|
238
266
|
|
|
239
267
|
### Browser Lifecycle (2)
|
|
240
268
|
|
|
@@ -283,12 +311,13 @@ That's all. Camoufox browser binary (~80MB) downloads automatically on first lau
|
|
|
283
311
|
| `type_text` | Type char by char. Options: `delay`. For OTP, masked inputs, date pickers. |
|
|
284
312
|
| `press_key` | Key or combo: `Enter`, `Escape`, `Tab`, `Control+a`, `Meta+c` |
|
|
285
313
|
|
|
286
|
-
### Mouse XY (
|
|
314
|
+
### Mouse XY (4)
|
|
287
315
|
|
|
288
316
|
| Tool | Description |
|
|
289
317
|
|------|-------------|
|
|
290
|
-
| `mouse_click_xy` | Click at exact coordinates |
|
|
291
|
-
| `mouse_move` | Move cursor to coordinates |
|
|
318
|
+
| `mouse_click_xy` | Click at exact coordinates. Optional `steps` (0=instant, 15-30=human-like pre-movement) |
|
|
319
|
+
| `mouse_move` | Move cursor to coordinates. Optional `steps` for interpolated path |
|
|
320
|
+
| `click_turnstile` | Auto-find + humanized click on Cloudflare Turnstile widget. Params: `offset_x` (default 30), `offset_y`, `wait_render_ms`. Works on Interactive Turnstile (visible iframe widget). Not for Managed Challenge interstitials. |
|
|
292
321
|
| `drag_and_drop` | Drag between two elements |
|
|
293
322
|
|
|
294
323
|
### Wait (4)
|
package/dist/index.js
CHANGED
|
@@ -804,21 +804,74 @@ server.tool("sessionstorage_set", "Set a sessionStorage item.", {
|
|
|
804
804
|
return { content: [{ type: "text", text: `sessionStorage set: ${key}` }] };
|
|
805
805
|
});
|
|
806
806
|
// ── Tools: Mouse XY ────────────────────────────────────────────────────────
|
|
807
|
-
server.tool("mouse_click_xy", "Click at exact x,y coordinates
|
|
807
|
+
server.tool("mouse_click_xy", "Click at exact x,y coordinates. steps>0 adds interpolated pre-movement (human-like).", {
|
|
808
808
|
x: z.number(), y: z.number(),
|
|
809
809
|
button: z.enum(["left", "right", "middle"]).default("left"),
|
|
810
|
-
|
|
810
|
+
steps: z.number().default(0).describe("Interpolation steps for pre-click movement (0=instant, 15-30=human-like)"),
|
|
811
|
+
}, async ({ x, y, button, steps }) => {
|
|
811
812
|
const page = getPage();
|
|
813
|
+
if (steps > 0) {
|
|
814
|
+
await page.mouse.move(x, y, { steps });
|
|
815
|
+
await page.waitForTimeout(80 + Math.random() * 60);
|
|
816
|
+
}
|
|
812
817
|
await page.mouse.click(x, y, { button });
|
|
813
818
|
await page.waitForTimeout(500);
|
|
814
|
-
return { content: [{ type: "text", text: `Clicked at (${x}, ${y}) button=${button}` }] };
|
|
819
|
+
return { content: [{ type: "text", text: `Clicked at (${x}, ${y}) button=${button} steps=${steps}` }] };
|
|
815
820
|
});
|
|
816
|
-
server.tool("mouse_move", "Move mouse to
|
|
821
|
+
server.tool("mouse_move", "Move mouse to x,y. steps>0 interpolates path (human-like).", {
|
|
817
822
|
x: z.number(), y: z.number(),
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
823
|
+
steps: z.number().default(0).describe("Interpolation steps (0=instant jump, 15-30=smooth)"),
|
|
824
|
+
}, async ({ x, y, steps }) => {
|
|
825
|
+
const page = getPage();
|
|
826
|
+
await page.mouse.move(x, y, steps > 0 ? { steps } : undefined);
|
|
827
|
+
return { content: [{ type: "text", text: `Mouse moved to (${x}, ${y}) steps=${steps}` }] };
|
|
828
|
+
});
|
|
829
|
+
server.tool("click_turnstile", "Auto-find and click Cloudflare Turnstile checkbox. Port of mcp-stealth-chrome's proven pattern — single pre-drift + direct click, leaning on Camoufox's built-in humanize + disable_coop for cross-origin iframe support. Works on Interactive Turnstile widgets (visible iframe). Managed Challenge interstitials not supported — use mcp-stealth-chrome for those.", {
|
|
830
|
+
offset_x: z.number().default(30).describe("Pixels from widget left edge (calibrated for CF checkbox)"),
|
|
831
|
+
offset_y: z.number().optional().describe("Vertical offset from widget top (default = height/2)"),
|
|
832
|
+
wait_render_ms: z.number().default(500).describe("Wait before detection to let widget render"),
|
|
833
|
+
}, async ({ offset_x, offset_y, wait_render_ms }) => {
|
|
834
|
+
const page = getPage();
|
|
835
|
+
await page.waitForTimeout(wait_render_ms);
|
|
836
|
+
// Widget detection — 6 selectors ordered by specificity (port from mcp-stealth-chrome)
|
|
837
|
+
const coords = await page.evaluate(() => {
|
|
838
|
+
const sels = [
|
|
839
|
+
'iframe[src*="challenges.cloudflare.com"]',
|
|
840
|
+
'iframe[src*="turnstile"]',
|
|
841
|
+
'[data-testid*="challenge-widget"]',
|
|
842
|
+
'[data-testid*="turnstile"]',
|
|
843
|
+
'[data-sitekey]',
|
|
844
|
+
'.cf-turnstile',
|
|
845
|
+
];
|
|
846
|
+
for (const sel of sels) {
|
|
847
|
+
const el = document.querySelector(sel);
|
|
848
|
+
if (!el)
|
|
849
|
+
continue;
|
|
850
|
+
const r = el.getBoundingClientRect();
|
|
851
|
+
if (r.width < 50 || r.height < 20)
|
|
852
|
+
continue;
|
|
853
|
+
return {
|
|
854
|
+
found: sel,
|
|
855
|
+
left: Math.round(r.left),
|
|
856
|
+
top: Math.round(r.top),
|
|
857
|
+
width: Math.round(r.width),
|
|
858
|
+
height: Math.round(r.height),
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
return null;
|
|
862
|
+
});
|
|
863
|
+
if (!coords) {
|
|
864
|
+
return { content: [{ type: "text", text: "Turnstile widget not found — selector miss. Likely a Managed Challenge interstitial (use mcp-stealth-chrome) or widget hasn't rendered yet (try wait_render_ms=3000)." }] };
|
|
865
|
+
}
|
|
866
|
+
const targetX = coords.left + offset_x;
|
|
867
|
+
const targetY = coords.top + (offset_y ?? Math.floor(coords.height / 2));
|
|
868
|
+
// Single pre-drift then direct click (matches stealth-chrome's pattern).
|
|
869
|
+
// Camoufox's humanize layer handles path curvature + timing automatically,
|
|
870
|
+
// so extra Bezier hops would be redundant and slow (~3s extra).
|
|
871
|
+
await page.mouse.move(targetX + 180, targetY - 80, { steps: 15 });
|
|
872
|
+
await page.waitForTimeout(150);
|
|
873
|
+
await page.mouse.click(targetX, targetY);
|
|
874
|
+
return { content: [{ type: "text", text: `clicked Turnstile at (${targetX},${targetY}) — widget found via ${coords.found} (${coords.width}x${coords.height})` }] };
|
|
822
875
|
});
|
|
823
876
|
server.tool("drag_and_drop", "Drag from one element to another.", {
|
|
824
877
|
source_ref: z.string().describe("Ref of element to drag"),
|
package/package.json
CHANGED