Skip to content

Commit b0cddcd

Browse files
author
Nima21
committed
Add stealth mode and custom Chrome args support
- Added puppeteer-extra and puppeteer-extra-plugin-stealth dependencies - Implemented --stealth flag to enable bot detection bypass - Added --chromeArgs option to pass custom Chrome arguments - Updated browser.ts to conditionally use puppeteer-extra with stealth - Added anti-detection Chrome flags when stealth is enabled - Updated CLI with new options and examples - Created comprehensive documentation in STEALTH_FEATURES.md This allows Chrome DevTools MCP to bypass bot detection on protected sites like Cloudflare-protected pages, e-commerce sites, etc.
1 parent 2714158 commit b0cddcd

5 files changed

Lines changed: 217 additions & 2 deletions

File tree

STEALTH_FEATURES.md

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# Stealth Mode Features
2+
3+
This fork of Chrome DevTools MCP adds **stealth mode** capabilities to bypass bot detection on websites.
4+
5+
## New Features
6+
7+
### 1. **Stealth Mode** (`--stealth`)
8+
Enables puppeteer-extra-plugin-stealth and adds anti-detection Chrome arguments to make browser automation undetectable.
9+
10+
**What it does:**
11+
- Uses `puppeteer-extra` with `puppeteer-extra-plugin-stealth` plugin
12+
- Removes automation-related Chrome arguments
13+
- Adds anti-detection Chrome flags:
14+
- `--disable-blink-features=AutomationControlled`
15+
- `--disable-features=IsolateOrigins,site-per-process`
16+
- `--no-first-run`
17+
- `--no-service-autorun`
18+
- `--password-store=basic`
19+
- Removes `--enable-automation` flag
20+
21+
### 2. **Custom Chrome Arguments** (`--chromeArgs`)
22+
Pass any additional Chrome command-line arguments (comma-separated).
23+
24+
## Usage
25+
26+
### Basic Stealth Mode
27+
```bash
28+
npx chrome-devtools-mcp@latest --stealth
29+
```
30+
31+
### Stealth Mode with Custom Chrome Arguments
32+
```bash
33+
npx chrome-devtools-mcp@latest --stealth --chromeArgs="--window-size=1920,1080,--user-agent=CustomAgent"
34+
```
35+
36+
### Only Custom Arguments (without stealth plugin)
37+
```bash
38+
npx chrome-devtools-mcp@latest --chromeArgs="--disable-gpu,--no-sandbox"
39+
```
40+
41+
## MCP Configuration
42+
43+
### Claude Code
44+
```bash
45+
claude mcp add chrome-stealth npx chrome-devtools-mcp@latest -- --stealth
46+
```
47+
48+
Or manually edit `.claude/config.json`:
49+
```json
50+
{
51+
"mcpServers": {
52+
"chrome-stealth": {
53+
"command": "npx",
54+
"args": [
55+
"chrome-devtools-mcp@latest",
56+
"--stealth"
57+
]
58+
}
59+
}
60+
}
61+
```
62+
63+
### With Custom Chrome Args
64+
```json
65+
{
66+
"mcpServers": {
67+
"chrome-stealth": {
68+
"command": "npx",
69+
"args": [
70+
"chrome-devtools-mcp@latest",
71+
"--stealth",
72+
"--chromeArgs=--window-size=1920,1080"
73+
]
74+
}
75+
}
76+
}
77+
```
78+
79+
### With Chromium Path
80+
```json
81+
{
82+
"mcpServers": {
83+
"chrome-stealth": {
84+
"command": "npx",
85+
"args": [
86+
"chrome-devtools-mcp@latest",
87+
"--stealth",
88+
"--executablePath",
89+
"/usr/bin/chromium"
90+
]
91+
}
92+
}
93+
}
94+
```
95+
96+
## Technical Details
97+
98+
### Modified Files
99+
- `package.json` - Added `puppeteer-extra` and `puppeteer-extra-plugin-stealth` dependencies
100+
- `src/browser.ts` - Added stealth mode logic and custom Chrome args support
101+
- `src/cli.ts` - Added `--stealth` and `--chromeArgs` CLI options
102+
- `src/main.ts` - Pass stealth options to browser launcher
103+
104+
### How Stealth Mode Works
105+
1. **Puppeteer-Extra Plugin**: Uses `puppeteer-extra-plugin-stealth` which applies dozens of evasion techniques:
106+
- Removes `navigator.webdriver` flag
107+
- Masks Chrome headless detection
108+
- Fixes `navigator.plugins` and `navigator.languages`
109+
- Spoofs WebGL vendor/renderer
110+
- And many more...
111+
112+
2. **Chrome Flags**: Adds critical anti-detection flags:
113+
- `--disable-blink-features=AutomationControlled` - Removes automation indicators
114+
- `ignoreDefaultArgs: ['--enable-automation']` - Prevents automation flag
115+
116+
3. **Conditional**: Only uses puppeteer-extra when stealth is enabled, otherwise uses standard puppeteer-core for better performance
117+
118+
## Testing
119+
120+
Test stealth mode on bot-protected sites:
121+
```bash
122+
# Navigate to bot-protected site
123+
npx chrome-devtools-mcp@latest --stealth
124+
125+
# Then use the MCP tools to navigate to sites like:
126+
# - https://2nabiji.ge
127+
# - https://bot.sannysoft.com
128+
# - https://arh.antoinevastel.com/bots/areyouheadless
129+
```
130+
131+
## Comparison with Standard Chrome DevTools MCP
132+
133+
| Feature | Standard | With Stealth |
134+
|---------|----------|--------------|
135+
| Bot Detection | ❌ Detected | ✅ Bypassed |
136+
| Performance | Fast | Slightly slower |
137+
| Dependencies | puppeteer-core | + puppeteer-extra |
138+
| Use Case | Development/Testing | Bot-protected sites |
139+
140+
## Use Cases
141+
142+
- **E-commerce scraping**: Access sites with Cloudflare or similar protection
143+
- **Price monitoring**: Automated price checking without detection
144+
- **Testing**: Test how your site appears to real users vs bots
145+
- **Research**: Access data from protected sources
146+
147+
## Credits
148+
149+
Stealth implementation based on:
150+
- [puppeteer-extra](https://github.com/berstend/puppeteer-extra)
151+
- [puppeteer-extra-plugin-stealth](https://github.com/berstend/puppeteer-extra/tree/master/packages/puppeteer-extra-plugin-stealth)
152+
153+
Original Chrome DevTools MCP by Google LLC / Chrome DevTools team.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
"core-js": "3.45.1",
4242
"debug": "4.4.3",
4343
"puppeteer-core": "24.22.3",
44+
"puppeteer-extra": "^3.3.6",
45+
"puppeteer-extra-plugin-stealth": "^2.11.2",
4446
"yargs": "18.0.0"
4547
},
4648
"devDependencies": {

src/browser.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ import type {
1616
Target,
1717
} from 'puppeteer-core';
1818
import puppeteer from 'puppeteer-core';
19+
import puppeteerExtra from 'puppeteer-extra';
20+
import StealthPlugin from 'puppeteer-extra-plugin-stealth';
21+
22+
// Add stealth plugin
23+
puppeteerExtra.use(StealthPlugin());
1924

2025
let browser: Browser | undefined;
2126

@@ -70,6 +75,8 @@ interface McpLaunchOptions {
7075
height: number;
7176
};
7277
args?: string[];
78+
stealth?: boolean;
79+
chromeArgs?: string[];
7380
}
7481

7582
export async function launch(options: McpLaunchOptions): Promise<Browser> {
@@ -96,6 +103,23 @@ export async function launch(options: McpLaunchOptions): Promise<Browser> {
96103
...(options.args ?? []),
97104
'--hide-crash-restore-bubble',
98105
];
106+
107+
// Add custom Chrome arguments if provided
108+
if (options.chromeArgs) {
109+
args.push(...options.chromeArgs);
110+
}
111+
112+
// Add stealth-enhancing arguments if stealth mode is enabled
113+
if (options.stealth) {
114+
args.push(
115+
'--disable-blink-features=AutomationControlled',
116+
'--disable-features=IsolateOrigins,site-per-process',
117+
'--no-first-run',
118+
'--no-service-autorun',
119+
'--password-store=basic',
120+
);
121+
}
122+
99123
if (customDevTools) {
100124
args.push(`--custom-devtools-frontend=file://${customDevTools}`);
101125
}
@@ -108,7 +132,10 @@ export async function launch(options: McpLaunchOptions): Promise<Browser> {
108132
}
109133

110134
try {
111-
const browser = await puppeteer.launch({
135+
// Use puppeteer-extra with stealth plugin if stealth mode is enabled
136+
const puppeteerInstance = options.stealth ? puppeteerExtra : puppeteer;
137+
138+
const launchOptions: LaunchOptions = {
112139
...connectOptions,
113140
channel: puppeteerChannel,
114141
executablePath,
@@ -118,7 +145,14 @@ export async function launch(options: McpLaunchOptions): Promise<Browser> {
118145
headless,
119146
args,
120147
acceptInsecureCerts: options.acceptInsecureCerts,
121-
});
148+
};
149+
150+
// Add ignoreDefaultArgs when stealth is enabled to remove automation flags
151+
if (options.stealth) {
152+
launchOptions.ignoreDefaultArgs = ['--enable-automation'];
153+
}
154+
155+
const browser = await puppeteerInstance.launch(launchOptions);
122156
if (options.logFile) {
123157
// FIXME: we are probably subscribing too late to catch startup logs. We
124158
// should expose the process earlier or expose the getRecentLogs() getter.

src/cli.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,21 @@ export const cliOptions = {
8181
type: 'boolean',
8282
description: `If enabled, ignores errors relative to self-signed and expired certificates. Use with caution.`,
8383
},
84+
stealth: {
85+
type: 'boolean',
86+
description: `Enable stealth mode to avoid bot detection. Uses puppeteer-extra-plugin-stealth and adds anti-detection Chrome arguments.`,
87+
default: false,
88+
},
89+
chromeArgs: {
90+
type: 'string',
91+
description: `Additional Chrome arguments to pass to the browser (comma-separated). Example: --chromeArgs="--disable-gpu,--no-sandbox"`,
92+
coerce: (arg: string | undefined) => {
93+
if (!arg) {
94+
return undefined;
95+
}
96+
return arg.split(',').map(a => a.trim());
97+
},
98+
},
8499
} satisfies Record<string, YargsOptions>;
85100

86101
export function parseArguments(version: string, argv = process.argv) {
@@ -110,6 +125,15 @@ export function parseArguments(version: string, argv = process.argv) {
110125
'$0 --viewport 1280x720',
111126
'Launch Chrome with the initial viewport size of 1280x720px',
112127
],
128+
['$0 --stealth', 'Enable stealth mode to bypass bot detection'],
129+
[
130+
'$0 --chromeArgs="--disable-gpu,--no-sandbox"',
131+
'Pass custom Chrome arguments',
132+
],
133+
[
134+
'$0 --stealth --chromeArgs="--window-size=1920,1080"',
135+
'Use stealth mode with custom window size',
136+
],
113137
]);
114138

115139
return yargsInstance

src/main.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ async function getContext(): Promise<McpContext> {
8585
viewport: args.viewport,
8686
args: extraArgs,
8787
acceptInsecureCerts: args.acceptInsecureCerts,
88+
stealth: args.stealth,
89+
chromeArgs: args.chromeArgs as string[] | undefined,
8890
});
8991

9092
if (context?.browser !== browser) {

0 commit comments

Comments
 (0)