# 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); }); ```