#
tokens: 2147/50000 6/6 files
lines: off (toggle) GitHub
raw markdown copy
# 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
[![MseeP.ai Security Assessment Badge](https://mseep.net/pr/newbeb-stealth-browser-mcp-badge.png)](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);
});
```