# Directory Structure
```
├── .gitignore
├── bun.lock
├── CLAUDE.md
├── index.ts
├── LICENSE
├── package.json
├── README.md
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
1 | # dependencies (bun install)
2 | node_modules
3 |
4 | # output
5 | out
6 | dist
7 | *.tgz
8 |
9 | # code coverage
10 | coverage
11 | *.lcov
12 |
13 | # logs
14 | logs
15 | _.log
16 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
17 |
18 | # dotenv environment variable files
19 | .env
20 | .env.development.local
21 | .env.test.local
22 | .env.production.local
23 | .env.local
24 |
25 | # caches
26 | .eslintcache
27 | .cache
28 | *.tsbuildinfo
29 |
30 | # IntelliJ based IDEs
31 | .idea
32 |
33 | # Finder (MacOS) folder config
34 | .DS_Store
35 |
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
1 | [](https://mseep.ai/app/newbeb-stealth-browser-mcp)
2 |
3 | # Stealth Browser MCP Server
4 |
5 | An [MCP (Model Context Protocol)](https://modelcontextprotocol.ai) server that provides stealth browser capabilities using Playwright with anti-detection techniques. This server allows MCP clients to navigate to websites and take screenshots while evading common bot detection systems.
6 |
7 | <a href="https://glama.ai/mcp/servers/efxcqjoq01">
8 | <img width="380" height="200" src="https://glama.ai/mcp/servers/efxcqjoq01/badge" alt="Stealth Browser Server MCP server" />
9 | </a>
10 |
11 | ## Features
12 |
13 | - **Stealth Mode**: Uses [puppeteer-extra-plugin-stealth](https://github.com/berstend/puppeteer-extra/tree/master/packages/puppeteer-extra-plugin-stealth) with [playwright-extra](https://github.com/berstend/puppeteer-extra/tree/master/packages/playwright-extra) to bypass bot detections
14 | - Modifies browser fingerprints to appear as regular user traffic
15 | - Handles WebGL, canvas, font, plugin and other browser fingerprinting techniques
16 | - **Screenshot Tool**: Take full-page or element-specific screenshots of any website
17 | - Supports both headless (default) and visible browser modes
18 | - **MCP Integration**: Exposes browser capabilities via Model Context Protocol
19 |
20 | ## Installation
21 |
22 | ```bash
23 | # Install dependencies
24 | bun install
25 | ```
26 |
27 | ## Usage
28 |
29 | ```bash
30 | # Run the MCP server
31 | bun start
32 |
33 | # Development mode
34 | bun dev
35 |
36 | # Inspect available tools
37 | bun inspect
38 | ```
39 |
40 | ## Available Tools
41 |
42 | ### screenshot
43 |
44 | Takes screenshots of webpages using a stealth browser.
45 |
46 | Parameters:
47 | - `url` (string, required): The URL to navigate to
48 | - `fullPage` (boolean, optional, default: true): Whether to capture the entire page
49 | - `selector` (string, optional): CSS selector to capture only a specific element
50 | - `headless` (boolean, optional, default: true): Whether to run in headless mode or visible browser mode
51 |
52 | ## Technical Details
53 |
54 | This project uses:
55 | - [FastMCP](https://github.com/punkpeye/fastmcp) for the MCP server implementation
56 | - [Playwright](https://playwright.dev/) for browser automation
57 | - [playwright-extra](https://github.com/berstend/puppeteer-extra/tree/master/packages/playwright-extra) for plugin support
58 | - [puppeteer-extra-plugin-stealth](https://github.com/berstend/puppeteer-extra/tree/master/packages/puppeteer-extra-plugin-stealth) for avoiding bot detection
59 |
60 | ---
61 |
62 | This project was built with [Bun](https://bun.sh), a fast all-in-one JavaScript runtime.
```
--------------------------------------------------------------------------------
/CLAUDE.md:
--------------------------------------------------------------------------------
```markdown
1 | # stealth-browser-mcp Guidelines
2 |
3 | ## Workflow
4 | - Use a git feature based workflow and always work in a feature and make sure we commit things along the way.
5 | - Don't mix features in a single branch. If we need to work on multiple features, create separate branches.
6 | - If we learn something along the way about how to work better please share it with me and if I agree you can add it to this file. Don't ever add anything here with my specific approval.
7 | - You've got tools, use them!
8 |
9 | ## Commands
10 | - **Run**: `bun run index.ts` - Run the main application
11 | - **Dev**: `bun --watch index.ts` - Run with live reloading
12 |
13 | ## Code Style
14 | - **Imports**: Use named imports, sort alphabetically, group by type
15 | - **Formatting**: 2-space indentation, trailing semicolons
16 | - **Types**: Prefer explicit typing with interfaces over types when possible
17 | - **Naming**: camelCase for variables/functions, PascalCase for classes/types
18 | - **Error Handling**: Use try/catch with specific error types, avoid generic catches
19 | - **Comments**: JSDoc for public APIs, inline for complex logic
20 | - **Async**: Prefer async/await over raw Promises
21 | - **Stealth Browser**: Follow puppeteer-extra plugin patterns for stealth features
22 |
23 | ## Project Structure
24 | - `index.ts`: Main application entry point
25 |
26 | This project uses Bun and TypeScript with ESM modules.
27 |
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "compilerOptions": {
3 | // Enable latest features
4 | "lib": ["ESNext", "DOM"],
5 | "target": "ESNext",
6 | "module": "ESNext",
7 | "moduleDetection": "force",
8 | "jsx": "react-jsx",
9 | "allowJs": true,
10 |
11 | // Bundler mode
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "verbatimModuleSyntax": true,
15 | "noEmit": true,
16 |
17 | // Best practices
18 | "strict": true,
19 | "skipLibCheck": true,
20 | "noFallthroughCasesInSwitch": true,
21 |
22 | // Some stricter flags (disabled by default)
23 | "noUnusedLocals": false,
24 | "noUnusedParameters": false,
25 | "noPropertyAccessFromIndexSignature": false
26 | }
27 | }
28 |
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "stealth-browser-mcp",
3 | "type": "module",
4 | "version": "0.0.1",
5 | "main": "index.ts",
6 | "module": "index.ts",
7 | "author": "Brian Lloyd-Newberry",
8 | "email": "[email protected]",
9 | "description": "A MCP Server that provides browser access through playwright with \"stealth mode\" enabled.",
10 | "scripts": {
11 | "start": "bun run index.ts",
12 | "dev": "fastmcp dev index.ts",
13 | "inspect": "fastmcp inspect index.ts"
14 | },
15 | "devDependencies": {
16 | "@modelcontextprotocol/inspector": "0.4.1",
17 | "@types/bun": "latest",
18 | "@wong2/mcp-cli": "1.4.0",
19 | "tsx": "^4.19.3"
20 | },
21 | "peerDependencies": {
22 | "typescript": "^5"
23 | },
24 | "dependencies": {
25 | "fastmcp": "^1.19.0",
26 | "playwright": "^1.50.1",
27 | "playwright-extra": "^4.3.6",
28 | "puppeteer-extra-plugin-stealth": "^2.11.2",
29 | "zod": "^3.24.2"
30 | }
31 | }
32 |
```
--------------------------------------------------------------------------------
/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { FastMCP } from 'fastmcp';
2 | import { z } from 'zod';
3 | import { chromium } from 'playwright-extra';
4 | import StealthPlugin from 'puppeteer-extra-plugin-stealth';
5 |
6 | // Add the stealth plugin to playwright
7 | chromium.use(StealthPlugin());
8 |
9 | // Create a new FastMCP server
10 | const server = new FastMCP({
11 | name: 'stealth-browser-mcp',
12 | version: '1.0.0'
13 | });
14 |
15 | // Add the screenshot tool
16 | server.addTool({
17 | name: 'screenshot',
18 | description: 'Navigate to a URL and take a screenshot of the webpage',
19 | parameters: z.object({
20 | url: z.string().describe('URL to navigate to'),
21 | fullPage: z.boolean().default(true).describe('Whether to take a screenshot of the full page'),
22 | selector: z.string().optional().describe('CSS selector to screenshot a specific element'),
23 | headless: z.boolean().default(true).describe('Whether to run browser in headless mode (default) or visible mode')
24 | }),
25 | execute: async ({ url, fullPage = true, selector, headless = true }) => {
26 | // Launch browser with stealth mode
27 | const browser = await chromium.launch({ headless });
28 | try {
29 | const page = await browser.newPage();
30 |
31 | // Navigate to the URL
32 | console.error(`Navigating to ${url}...`);
33 | await page.goto(url, { waitUntil: 'networkidle' });
34 |
35 | // Take the screenshot
36 | const screenshotOptions = { fullPage };
37 | let screenshot;
38 |
39 | if (selector) {
40 | // Screenshot specific element if selector is provided
41 | const element = await page.$(selector);
42 | if (element) {
43 | screenshot = await element.screenshot();
44 | } else {
45 | throw new Error(`Element with selector '${selector}' not found`);
46 | }
47 | } else {
48 | // Screenshot entire page
49 | screenshot = await page.screenshot(screenshotOptions);
50 | }
51 |
52 | // Return screenshot as base64 data with content type
53 | return {
54 | content: [
55 | {
56 | type: 'image',
57 | data: screenshot.toString('base64'),
58 | mimeType: 'image/png'
59 | }
60 | ]
61 | };
62 | } finally {
63 | await browser.close();
64 | }
65 | }
66 | });
67 |
68 | // Start the server with STDIO transport
69 | server.start({ transportType: 'stdio' }).then(() => {
70 | console.error('MCP server started and waiting for commands...');
71 | }).catch(error => {
72 | console.error('Failed to start MCP server:', error);
73 | });
```