Command Palette

Search for a command to run...

UnylyUnyly
Browse all

Cf Browser

FreeNot checked

MCP Server with 15 browser tools for Claude Code — powered by Cloudflare Browser Rendering

GitHubEmbed

About

MCP Server with 15 browser tools for Claude Code — powered by Cloudflare Browser Rendering

README

The fastest way to read any website from Claude Code.

Open-source tool that gives Claude Code 15 MCP tools + 6 ready-to-use Skills for JavaScript-rendered web pages — content extraction, screenshots, PDFs, accessibility snapshots, AI-powered data extraction, multi-page crawling, and browser interaction (click, type, form submit, JS eval, action chains). Powered by Cloudflare Browser Rendering with zero-cost free tier. Supports Direct Mode (no Worker needed) and Worker Mode (with caching, rate limiting, and interaction).

PyPI - cf-browser PyPI - cf-browser-mcp Tests License: MIT

Why CF Browser?

Claude Code's built-in WebFetch only returns raw HTML. Single-page apps, dynamic content, and JS-rendered pages come back empty. CF Browser solves this:

  • JS execution — full headless Chrome renders the page before extraction
  • 15 purpose-built tools — markdown, screenshots, PDFs, accessibility snapshots, AI extraction, crawling, plus click/type/evaluate/interact/form-submit
  • Browser interaction — click buttons, fill forms, execute JS, chain multi-step actions (Worker mode)
  • Authenticated scraping — inject cookies and custom headers for logged-in pages
  • Zero cost — read-only tools run on Cloudflare's free tier; interaction tools require Workers Paid ($5/mo)
  • Edge-based — global low latency from 300+ Cloudflare locations

Quick Start

Two ways to use CF Browser — pick the one that fits:

Direct Mode Worker Mode
Setup pip install + 2 env vars Deploy Worker + pip install
Time to start 2 minutes 10 minutes
Requirements CF Account ID + API Token Worker + KV + R2
Available tools 10 read-only tools All 15 tools
Caching None KV + R2 (saves ~70% API quota)
Rate limiting None 60 req/min per key
Multi-user No (shares your CF credentials) Yes (each user gets own API key)
Best for Personal use, quick start Teams, production, high volume

Option A: Direct Mode (No Worker)

Calls Cloudflare Browser Rendering API directly — no Worker deployment needed.

pip install cf-browser cf-browser-mcp

Add to your .mcp.json:

{
  "mcpServers": {
    "cf-browser": {
      "type": "stdio",
      "command": "python3",
      "args": ["-m", "cf_browser_mcp.server"],
      "env": {
        "CF_ACCOUNT_ID": "<your-account-id>",
        "CF_API_TOKEN": "<your-api-token>"
      }
    }
  }
}

Get your credentials:

Restart Claude Code. The 10 read-only tools work immediately; the 5 interaction tools require Worker Mode.

Option B: Worker Mode (with caching & rate limiting)

Deploy a Cloudflare Worker as an edge proxy with built-in caching and auth.

One-Command Setup:

git clone https://github.com/claude-world/cf-browser.git
cd cf-browser
bash setup.sh

The setup script creates all Cloudflare resources, deploys the Worker, installs Python packages, and outputs a ready-to-paste .mcp.json config.

Click to expand manual Worker setup

Prerequisites

  • Node.js 18+, Python 3.10+
  • Cloudflare account with Browser Rendering enabled
  • wrangler CLI authenticated (npm i -g wrangler && wrangler login)

Step 1: Deploy the Worker

cd worker
cp wrangler.toml.example wrangler.toml
npm install

Create resources and paste the namespace IDs into wrangler.toml:

wrangler kv namespace create CACHE
wrangler kv namespace create RATE_LIMIT
wrangler r2 bucket create cf-browser-storage

Set secrets:

wrangler secret put CF_ACCOUNT_ID      # from: wrangler whoami
wrangler secret put CF_API_TOKEN       # from: https://dash.cloudflare.com/profile/api-tokens
echo "$(openssl rand -hex 32)" | wrangler secret put API_KEYS

Deploy:

wrangler deploy
# → https://cf-browser.<your-subdomain>.workers.dev

Step 2: Install SDK + MCP Server

pip install cf-browser cf-browser-mcp

Or install from source:

cd sdk && pip install -e .
cd ../mcp-server && pip install -e .

Step 3: Register MCP in Claude Code

Add to your project's .mcp.json:

{
  "mcpServers": {
    "cf-browser": {
      "type": "stdio",
      "command": "python3",
      "args": ["-m", "cf_browser_mcp.server"],
      "env": {
        "CF_BROWSER_URL": "https://cf-browser.<your-subdomain>.workers.dev",
        "CF_BROWSER_API_KEY": "<your-api-key>"
      }
    }
  }
}

Restart Claude Code. You'll see 15 browser_* tools available.

Architecture

                          ┌─────────────────────┐
                          │     Claude Code      │
                          └──────────┬───────────┘
                                     │
                          ┌──────────▼───────────┐
                          │  MCP Server (15 tools)│
                          └──────────┬───────────┘
                                     │
                    ┌────────────────┴────────────────┐
                    │                                  │
            Direct Mode                        Worker Mode
            (CF_ACCOUNT_ID                     (CF_BROWSER_URL
             + CF_API_TOKEN)                    + CF_BROWSER_API_KEY)
                    │                                  │
                    │                    ┌─────────────▼──────────────┐
                    │                    │   Cloudflare Worker        │
                    │                    │  ├── Auth (timing-safe)    │
                    │                    │  ├── Rate limit (KV)       │
                    │                    │  └── Cache (KV + R2)       │
                    │                    └─────────────┬──────────────┘
                    │                                  │
                    └────────────────┬─────────────────┘
                                     │
                          ┌──────────▼───────────┐
                          │ CF Browser Rendering │
                          │   API (Chrome)       │
                          └──────────────────────┘

Three independent packages:

Package Language Purpose
worker/ TypeScript (Hono + Puppeteer) Edge proxy with auth, cache, rate limiting, browser interaction
sdk/ (cf-browser on PyPI) Python (httpx) Async client library
mcp-server/ (cf-browser-mcp on PyPI) Python (FastMCP) 15 MCP tools for Claude Code

MCP Tools

Read-only tools (Direct + Worker mode)

Tool Input Output Use case
browser_markdown url Markdown string Read any web page as clean text
browser_content url HTML string Get fully rendered HTML (JS executed)
browser_screenshot url, width, height PNG file path Visual verification, multi-device testing
browser_pdf url, format PDF file path Generate reports, archive pages
browser_scrape url, selectors[] {"elements":[...]} Extract selector matches with normalized metadata
browser_json url, prompt JSON AI-powered structured data extraction
browser_links url [{href, text}] Discover all hyperlinks on a page
browser_a11y url JSON Accessibility-oriented snapshot with screenshot stripped
browser_crawl url, limit {"job_id","status"} Start async multi-page crawl
browser_crawl_status job_id, wait JSON Poll or wait for crawl results

Interaction tools (Worker mode only — requires BROWSER binding)

Tool Input Output Use case
browser_click url, selector JSON Click a button/link and get resulting page
browser_type url, selector, text JSON Type into input fields
browser_evaluate url, script JSON Execute JavaScript and get return value
browser_interact url, actions[] JSON Chain multiple actions (click, type, wait, screenshot, etc.)
browser_submit_form url, fields JSON Fill and submit forms in one call

All tools accept optional cookies, headers, wait_for, wait_until, and user_agent parameters. Use wait_until="networkidle0" for SPA sites (React, Next.js, X/Twitter).

Examples in Claude Code

"Read the React 19 migration guide"
→ browser_markdown("https://react.dev/blog/2024/12/05/react-19")

"Show me what our homepage looks like on mobile"
→ browser_screenshot("https://example.com", width=375, height=667)

"Extract the top 5 products with name, price, and rating"
→ browser_json("https://example.com/products", prompt="Extract top 5 products...")

"Get the page structure for accessibility analysis"
→ browser_a11y("https://example.com")

"Scrape our dashboard (requires login)"
→ browser_markdown("https://app.example.com/dashboard", cookies='[{"name":"session","value":"abc"}]')

"Find all broken links on our site"
→ browser_crawl("https://example.com", limit=50) → browser_crawl_status(job_id, wait=True)

"Log into our staging site and check the dashboard"
→ browser_interact("https://staging.example.com/login", actions=[
    {"action":"type", "selector":"#email", "text":"[email protected]"},
    {"action":"type", "selector":"#password", "text":"secret"},
    {"action":"click", "selector":"button[type=submit]"},
    {"action":"wait", "selector":".dashboard"},
    {"action":"screenshot"}
  ])

"Fill out the contact form"
→ browser_submit_form("https://example.com/contact",
    fields={"#name":"Claude", "#email":"[email protected]", "#message":"Hello!"},
    submit_selector="button.submit")

Worker API Reference

All routes (except /health) require Authorization: Bearer <api-key> header.

Endpoints

Route Method Body Cache Response
/health GET {"status":"ok","version":"2.0.1","capabilities":{"interact":...}}
/content POST {url, wait_for?, wait_until?, user_agent?, cookies?, headers?, no_cache?} KV 1hr HTML
/markdown POST {url, wait_for?, wait_until?, user_agent?, cookies?, headers?, no_cache?} KV 1hr Markdown
/screenshot POST {url, width?, height?, full_page?, wait_for?, wait_until?, user_agent?, cookies?, headers?, no_cache?} R2 24hr PNG
/pdf POST {url, format?, landscape?, wait_for?, wait_until?, user_agent?, cookies?, headers?, no_cache?} R2 24hr PDF
/snapshot POST {url, wait_for?, wait_until?, user_agent?, cookies?, headers?, no_cache?} KV 30min JSON
/scrape POST {url, elements[], wait_for?, wait_until?, user_agent?, cookies?, headers?, no_cache?} KV 30min {"elements":[...]}
/json POST {url, prompt, schema?, wait_for?, wait_until?, user_agent?, cookies?, headers?, no_cache?} None JSON
/links POST {url, wait_for?, wait_until?, user_agent?, cookies?, headers?, no_cache?} KV 1hr [{href, text}]
/a11y POST {url, wait_for?, wait_until?, user_agent?, cookies?, headers?, no_cache?} KV 5min {"type":"accessibility_snapshot", ...}
/crawl POST {url, limit?, user_agent?, cookies?, headers?, no_cache?} {"job_id":"..."}
/crawl/:id GET R2 JSON
/crawl/:id DELETE 204 No Content
/click POST {url, selector, wait_for?, ...} None JSON
/type POST {url, selector, text, clear?, wait_for?, ...} None JSON
/evaluate POST {url, script, wait_for?, ...} None JSON
/interact POST {url, actions[], wait_for?, ...} None JSON
/submit-form POST {url, fields, submit_selector?, wait_for?, ...} None JSON

Interaction routes (/click, /type, /evaluate, /interact, /submit-form) require the BROWSER binding. They return 501 if the binding is not configured. If these routes return 404 instead, the Worker deployment is stale; redeploy and verify /health reports version: "2.0.1".

Response shapes are normalized across Worker, SDK, and MCP:

  • /scrape returns {"elements":[{"selector":"...", "results":[...]}]} even if the upstream API returns a raw list.
  • /links returns an array of {href, text} objects; bare URL strings are promoted to {href, text: null}.
  • /a11y is derived from /snapshot, strips base64 screenshot payloads, and adds type: "accessibility_snapshot".

Authenticated requests

All endpoints accept optional cookies and headers fields for accessing authenticated pages:

curl -X POST https://cf-browser.example.workers.dev/markdown \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://app.example.com/dashboard",
    "cookies": [{"name": "session_id", "value": "abc123", "domain": ".example.com"}],
    "headers": {"X-Custom-Auth": "token"}
  }'

Request examples

# Get markdown
curl -X POST https://cf-browser.example.workers.dev/markdown \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://react.dev"}'

# Screenshot with viewport
curl -X POST https://cf-browser.example.workers.dev/screenshot \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "width": 1280, "height": 720}' \
  -o screenshot.png

# Accessibility snapshot
curl -X POST https://cf-browser.example.workers.dev/a11y \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}'

# AI extraction
curl -X POST https://cf-browser.example.workers.dev/json \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://news.ycombinator.com", "prompt": "Extract top 5 stories with title and score"}'

Cache behavior

  • Set "no_cache": true in the request body to bypass cache
  • Cached responses include X-Cache: HIT header
  • Text content (HTML, Markdown, JSON) is stored in KV
  • Binary content (PNG, PDF) is stored in R2
  • Completed crawl results are persisted to R2

Rate limiting

  • Default: 60 requests per minute per API key
  • Response headers: X-RateLimit-Limit, X-RateLimit-Remaining
  • Exceeded: HTTP 429 with Retry-After header

Python SDK

pip install cf-browser
# Direct mode — no Worker needed
from cf_browser import CFBrowserDirect

async with CFBrowserDirect(
    account_id="your-cf-account-id",
    api_token="your-cf-api-token",
) as browser:
    md = await browser.markdown("https://example.com")

# Worker mode — via deployed Worker
from cf_browser import CFBrowser

async with CFBrowser(
    base_url="https://cf-browser.example.workers.dev",
    api_key="your-key",
) as browser:
    # Read a page
    markdown = await browser.markdown("https://react.dev")

    # Take a screenshot
    png_bytes = await browser.screenshot("https://example.com", width=1280, height=720)

    # AI-powered extraction
    data = await browser.json_extract(
        "https://news.ycombinator.com",
        prompt="Extract the top 5 stories with title and score",
    )

    # Accessibility snapshot (LLM-friendly, screenshot stripped)
    tree = await browser.a11y("https://example.com")

    # Scrape by CSS selectors
    elements = await browser.scrape("https://example.com", selectors=["h1", ".price"])

    # Authenticated scraping with cookies
    md = await browser.markdown(
        "https://app.example.com/dashboard",
        cookies=[{"name": "session", "value": "abc", "domain": ".example.com"}],
    )

    # Async crawl
    job_id = await browser.crawl("https://example.com", limit=10)
    result = await browser.crawl_wait(job_id, timeout=120)

SDK methods

Read-only (Direct + Worker mode):

Method Returns Description
content(url, **opts) str Rendered HTML
markdown(url, **opts) str Clean Markdown
screenshot(url, **opts) bytes PNG image
pdf(url, **opts) bytes PDF document
snapshot(url, **opts) dict HTML + metadata
scrape(url, selectors, **opts) dict Normalized as {"elements": [...]}
json_extract(url, prompt, **opts) dict AI-extracted data
links(url, **opts) list[dict] Normalized list of {href, text} objects
a11y(url, **opts) dict Accessibility-oriented snapshot with screenshot stripped
crawl(url, **opts) str Job ID
crawl_status(job_id) dict Job status
crawl_wait(job_id, timeout, poll_interval) dict Wait for completion

Interaction (Worker mode only):

Method Returns Description
click(url, selector, **opts) dict Click element, return page state
type_text(url, selector, text, clear?, **opts) dict Type into input field
evaluate(url, script, **opts) dict Execute JS, return result
interact(url, actions, **opts) dict Chain multiple actions
submit_form(url, fields, submit_selector?, **opts) dict Fill and submit form
delete_crawl(job_id) None Delete cached crawl result

All methods accept no_cache=True to bypass caching, cookies/headers for authenticated access, wait_for to wait for a CSS selector, wait_until for navigation strategy (networkidle0 for SPAs), and user_agent for custom User-Agent. Interaction methods raise NotImplementedError in Direct mode. In Worker mode, 404 Not Found on interaction methods usually means you are pointing at a stale Worker deployment and should redeploy.

Security

  • Auth: Timing-safe Bearer token comparison using SHA-256 (prevents timing attacks)
  • Rate limiting: Per-key tracking with hashed key material in KV (no raw keys stored)
  • SSRF prevention: Only http:// and https:// URLs allowed; localhost, private IP literals, and hostnames that DNS-resolve to private IPs are blocked
  • Secrets: All credentials stored via wrangler secret put, never in code
  • Cookie isolation: Cookies are injected per-request, never persisted

Skills (Bonus)

CF Browser includes 6 ready-to-use Claude Code Skills in the skills/ directory. Copy a skill folder to your project's .claude/skills/ to activate.

Skill Command What it does
content-extractor /content-extractor Read pages, extract structured data, scrape elements, discover links
site-auditor /site-auditor Crawl a site and generate SEO / link / accessibility audit report
doc-fetcher /doc-fetcher Crawl an entire docs site to local Markdown for RAG
visual-qa /visual-qa Multi-device viewport screenshots (mobile/tablet/laptop/desktop) + visual checks
changelog-monitor /changelog-monitor Track version updates and breaking changes for any project
competitor-watch /competitor-watch Extract and compare competitor pricing / features
# Copy a single skill
cp -r skills/content-extractor .claude/skills/

# Or copy all
cp -r skills/* .claude/skills/

Cost

Component Free Tier Paid ($5/mo Workers)
Browser Rendering 10 min/day, 5 crawl jobs Higher limits
KV 100K reads/day 10M reads/mo
R2 10GB storage 10GB included
Workers 100K requests/day 10M requests/mo

For most Claude Code usage, the free tier is sufficient. Interaction tools (click, type, evaluate, interact, submit-form) require the Workers Paid plan ($5/mo) for the BROWSER binding.

Troubleshooting

  • browser_click / browser_type / browser_evaluate / browser_interact / browser_submit_form return 501: the Worker is deployed without [browser] binding = "BROWSER".
  • Those same tools return 404: the Worker deployment is older than the current repo. Redeploy and verify /health returns version: "2.0.1".
  • browser_scrape or browser_links look different between environments: current SDK and MCP normalize legacy upstream shapes, but the cleanest fix is still to redeploy the Worker.

Development

Worker

cd worker
npm install
npm run dev          # Local dev server at :8787
npm run type-check   # TypeScript checks
npm test             # Run tests

SDK

cd sdk
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest tests/ -v

MCP Server

cd mcp-server
python -m venv .venv && source .venv/bin/activate
pip install -e ../sdk     # Install SDK first
pip install -e ".[dev]"
pytest tests/ -v

Project Structure

cf-browser/
├── worker/                  Cloudflare Worker (TypeScript)
│   ├── src/
│   │   ├── index.ts         Hono app entry point
│   │   ├── types.ts         Env bindings & request types
│   │   ├── middleware/
│   │   │   ├── auth.ts      Bearer token validation
│   │   │   ├── cache.ts     KV/R2 cache layer
│   │   │   └── rate-limit.ts  Per-key rate limiting
│   │   ├── routes/
│   │   │   ├── content.ts   POST /content → HTML
│   │   │   ├── markdown.ts  POST /markdown → Markdown
│   │   │   ├── screenshot.ts POST /screenshot → PNG
│   │   │   ├── pdf.ts       POST /pdf → PDF
│   │   │   ├── snapshot.ts  POST /snapshot → JSON
│   │   │   ├── scrape.ts    POST /scrape → JSON
│   │   │   ├── json.ts      POST /json → JSON (AI)
│   │   │   ├── links.ts     POST /links → JSON
│   │   │   ├── a11y.ts      POST /a11y → JSON (accessibility snapshot)
│   │   │   ├── crawl.ts     POST/GET/DELETE /crawl
│   │   │   ├── click.ts     POST /click (interaction)
│   │   │   ├── type.ts      POST /type (interaction)
│   │   │   ├── evaluate.ts  POST /evaluate (interaction)
│   │   │   ├── interact.ts  POST /interact (action chains)
│   │   │   └── submit-form.ts POST /submit-form (interaction)
│   │   └── lib/
│   │       ├── cf-api.ts    CF Browser Rendering client
│   │       ├── puppeteer.ts Puppeteer lifecycle helper (interaction)
│   │       ├── param-map.ts snake_case → CF API camelCase mapping
│   │       ├── response-normalizers.ts scrape/links response normalization
│   │       ├── cache-key.ts SHA-256 cache keys
│   │       └── validate-url.ts  SSRF prevention
│   ├── tests/
│   ├── wrangler.toml.example
│   └── package.json
├── sdk/                     Python SDK (cf-browser on PyPI)
│   ├── src/cf_browser/
│   │   ├── client.py        CFBrowser client (Worker mode)
│   │   ├── direct.py        CFBrowserDirect client (Direct mode)
│   │   ├── _normalizers.py  Response-shape normalization helpers
│   │   ├── _shared.py       Shared helpers (crawl polling)
│   │   ├── models.py        Pydantic response models
│   │   └── exceptions.py    Typed error hierarchy
│   ├── tests/
│   └── pyproject.toml
├── mcp-server/              MCP Server (cf-browser-mcp on PyPI)
│   ├── src/cf_browser_mcp/
│   │   └── server.py        15 MCP tool definitions
│   └── pyproject.toml
├── examples/                Usage examples
├── setup.sh                 One-command setup script
├── CHANGELOG.md
├── LICENSE
└── README.md

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes with tests
  4. Run npm test (worker) and pytest (SDK + MCP Server) to verify
  5. Submit a pull request

License

MIT

from github.com/claude-world/cf-browser

Installing Cf Browser

This server has no published package — it is built from source. Open the repository and follow its README.

▸ github.com/claude-world/cf-browser

FAQ

Is Cf Browser MCP free?

Yes, Cf Browser MCP is free — one-click install via Unyly at no cost.

Does Cf Browser need an API key?

No, Cf Browser runs without API keys or environment variables.

Is Cf Browser hosted or self-hosted?

Self-hosted: the server runs locally on your machine via the install command above.

How do I install Cf Browser in Claude Desktop, Claude Code or Cursor?

Open Cf Browser on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.

Related MCPs

Compare Cf Browser with

Not sure what to pick?

Find your stack in 60 seconds

Author?

Embed badge for your README

Browse similar

All development MCPs