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