# Give Claude Code web access: Playwright MCP, a scraping MCP, or both

Playwright MCP gives Claude Code a browser it drives step by step; a scraping MCP returns the finished page as Markdown or JSON in one call. Use Playwright for clicking through flows and a scraping MCP for reading pages; this guide installs both and runs the same task each way.

Updated 1 Oct 2026 · Tested on 1 Oct 2026 · https://skryp.dev/guides/claude-code-web-access

## Install either one, or both

Playwright MCP runs a browser on your machine that Claude Code drives step by step. A scraping MCP server, such as Skryp, fetches pages on its side and returns the finished content. Each is one command:

```bash
# Playwright MCP: a local browser (needs Node.js)
claude mcp add playwright -- npx @playwright/mcp@latest

# Skryp: pages fetched and cleaned for you (needs an API key)
claude mcp add --transport http skryp https://api.skryp.dev/mcp \
  --header "Authorization: Bearer $SKRYP_API_KEY"
```

Both commands were run on 1 October 2026; Skryp's connection was tested end to end in Claude Code [checked 1 Oct 2026].  Run `claude mcp list` to check that each server is connected; servers you add to a project's `.mcp.json` ask for your approval the first time you start `claude` there.

## The same tasks, three ways

We gave Claude Code three tasks with Playwright MCP only, Skryp only, and both, in fresh sessions with Claude Code's own web tools switched off, so the MCP server was the only way to the web. The script and what it printed:

```python
# The same three tasks in Claude Code with Playwright MCP, with Skryp's MCP server, and with both:
# did it get the answer, how many turns, how many tokens, what it cost, how long it took.
# Needs Claude Code, Node.js (for npx) and SKRYP_API_KEY. Each run is a fresh session in a scratch folder with
# Claude Code's built-in tools switched off (--tools ""), so the MCP servers are the only way to the web.
# timeout: 2400
import json
import os
import subprocess
import tempfile
import time

SKRYP = os.environ.get("SKRYP_API_URL", "https://api.skryp.dev")
SERVERS = {
    "playwright": {"command": "npx", "args": ["@playwright/mcp@latest", "--headless", "--isolated"]},
    "skryp": {"type": "http", "url": f"{SKRYP}/mcp", "headers": {"Authorization": f"Bearer {os.environ['SKRYP_API_KEY']}"}},
}
SETUPS = {"Playwright MCP": ["playwright"], "Skryp": ["skryp"], "Both": ["playwright", "skryp"]}
TASKS = {
    "Read a JavaScript page": ("Open https://quotes.toscrape.com/js/ and list the authors of the first five quotes, one per line.",
                               lambda a: all(n in a for n in ("Albert Einstein", "J.K. Rowling", "Jane Austen", "Marilyn Monroe"))),
    "Fill in a search form": ("On https://quotes.toscrape.com/search.aspx choose the author Albert Einstein and the tag inspirational, "
                              "search, and give me the first eight words of the quote it finds.",
                              lambda a: "There are only two ways to live your life" in a.replace("“", "")),
    "Price as seen in the UK": ("What does Portal 2 cost on Steam for a shopper in the United Kingdom? Answer with the price in pounds.",
                                lambda a: uk_price() in a),
}

def uk_price() -> str:
    """The price Steam itself reports for the UK right now (e.g. "1.70"), to mark the answers against."""
    import urllib.request
    url = "https://store.steampowered.com/api/appdetails?appids=620&cc=gb&filters=price_overview"
    data = json.load(urllib.request.urlopen(url, timeout=30))
    return f"{data['620']['data']['price_overview']['final'] / 100:.2f}"

def run(prompt: str, servers: list[str]) -> dict:
    with tempfile.TemporaryDirectory() as d:
        cfg = os.path.join(d, "mcp.json")
        json.dump({"mcpServers": {s: SERVERS[s] for s in servers}}, open(cfg, "w"))
        t0 = time.monotonic()
        p = subprocess.run(["claude", "-p", "--tools", "", "--setting-sources", "project", "--mcp-config", cfg, "--strict-mcp-config",
                            "--allowedTools", ",".join(f"mcp__{s}" for s in servers),
                            "--output-format", "stream-json", "--verbose", prompt], cwd=d, capture_output=True, text=True, timeout=600, stdin=subprocess.DEVNULL)
        secs = time.monotonic() - t0
    tools, urls, result = [], [], {}
    for line in p.stdout.splitlines():
        try:
            e = json.loads(line)
        except ValueError:
            continue
        if e.get("type") == "assistant":
            for c in e["message"]["content"]:
                if c.get("type") == "tool_use" and c["name"].startswith("mcp__"):
                    tools.append(c["name"].split("__")[1])
                    if c["input"].get("url"):
                        urls.append(c["input"]["url"] + (f" (country {c['input']['country']})" if c["input"].get("country") else ""))
        if e.get("type") == "result":
            result = e
    u = result.get("usage", {})
    tokens = u.get("input_tokens", 0) + u.get("cache_read_input_tokens", 0) + u.get("cache_creation_input_tokens", 0)
    return {"answer": result.get("result", ""), "turns": result.get("num_turns"), "tokens_in": tokens, "cost": result.get("total_cost_usd"),
            "secs": secs, "servers_used": sorted(set(tools)), "urls": urls}

print(f"Steam's own UK price for Portal 2 at the start: £{uk_price()}\n")
print(f"{'task':<24}{'setup':<16}{'answer ok':>10}{'turns':>7}{'input tokens':>14}{'cost $':>8}{'seconds':>9}  servers used")
for task, (prompt, ok) in TASKS.items():
    for setup, servers in SETUPS.items():
        r = run(prompt, servers)
        print(f"{task:<24}{setup:<16}{'yes' if ok(r['answer']) else 'no':>10}{r['turns'] or 0:>7}{r['tokens_in']:>14,}{(r['cost'] or 0):>8.2f}"
              f"{r['secs']:>9.0f}  {', '.join(r['servers_used']) or 'none'}")
        if task == "Price as seen in the UK":
            print(f"{'':<40}opened: {'; '.join(r['urls'][:2]) or '(nothing)'}")
            print(f"{'':<40}answer: {r['answer'].strip().splitlines()[0][:110] if r['answer'].strip() else '(none)'}")
```

Output (ran 1 Oct 2026; Python 3.12.14; 128.0 s):

```text
Steam's own UK price for Portal 2 at the start: £1.70

task                    setup            answer ok  turns  input tokens  cost $  seconds  servers used
Read a JavaScript page  Playwright MCP         yes      3        43,422    0.02       11  playwright
Read a JavaScript page  Skryp                  yes      2        49,594    0.03        6  skryp
Read a JavaScript page  Both                   yes      3        62,672    0.03       10  playwright
Fill in a search form   Playwright MCP         yes      6       109,877    0.05       18  playwright
Fill in a search form   Skryp                  yes      7       127,219    0.07       22  skryp
Fill in a search form   Both                   yes      6       190,163    0.07       21  playwright
Price as seen in the UK Playwright MCP         yes      3        65,036    0.03       11  playwright
                                        opened: https://store.steampowered.com/api/appdetails?appids=620&cc=gb&filters=price_overview
                                        answer: Portal 2 costs **£1.70** on the UK Steam store right now. It's 80% off its usual price of £8.50, so it will go
Price as seen in the UK Skryp                  yes      2        49,324    0.03       18  skryp
                                        opened: https://store.steampowered.com/api/appdetails?appids=620&cc=gb&filters=price_overview (country GB)
                                        answer: Portal 2 costs **£1.70** on Steam in the UK right now. That's 80% off its usual price of **£8.50**.
Price as seen in the UK Both                   yes      2        62,905    0.03        9  skryp
                                        opened: https://store.steampowered.com/api/appdetails?appids=620&cc=gb&filters=price_overview
                                        answer: Portal 2 costs **£1.70** on Steam in the UK right now. That's 80% off the normal price of £8.50.
```

What the runs show:

- **Reading a page:** both got the authors right. Skryp answered after one tool call in about half the time; Playwright needed one more step.
- **Filling in a form:** both succeeded. Playwright took fewer turns and fewer tokens, because driving a page step by step is what it is built for; Skryp took seven turns.
- **A price in another country:** every setup found Steam's own price feed and asked it for the UK with `cc=gb`. When a site lets you choose the country in the address, no proxy is needed; an exit in the country matters for sites that price by visitor location alone. For those, Skryp fetches from a checked exit there; see [price scraping by country](https://skryp.dev/solutions/price-scraping).
- **Both servers at once:** Claude used Playwright for the page and the form and Skryp for the price, and the same tasks used 28% to 73% more input tokens than with the cheaper single server.

The cost column is what Claude Code reported for each session and depends heavily on prompt caching between runs; compare the token and turn counts instead.

## Which to use

| You want Claude Code to | Use | Why |
| --- | --- | --- |
| Click through flows, fill forms, test your own app | Playwright MCP | Step-by-step control of a real browser, free, on your machine |
| Read pages, docs and articles | A scraping MCP server | One call returns clean Markdown; long pages are read in sections |
| Get pages that block automated browsers, or as seen from another country | A scraping MCP server | Routes and exits you do not have locally; Playwright uses your own connection |
| Both kinds of work in one project | Both | Expect more tokens per turn; add each server only to the projects that need it |

Playwright MCP is free and runs locally. A scraping server costs per page: Skryp charges 1 credit a page on its own network, 5 or 10 through a residential IP, with 1,000 free a month and nothing for failed pages. See [AI web scraping](https://skryp.dev/solutions/ai-web-scraping) for what Skryp's 17 tools do.

## Keep tool definitions lean

Claude Code defers MCP tool definitions by default and loads a server's tools when Claude looks for them [checked 1 Oct 2026]. Loaded definitions and tool results still take up context, as the runs above show, and other clients may load every tool up front. Keep servers scoped to the projects that need them (`claude mcp add --scope project`), remove the ones you no longer use with `claude mcp remove`, and check what is connected with `/mcp` inside Claude Code. [The best MCP servers for Claude Code](https://skryp.dev/guides/best-mcp-servers-claude-code) lists how large each server's tool definitions are.

## Questions

**What is Playwright MCP?**

An MCP server from Microsoft's Playwright team that lets an AI assistant such as Claude Code control a browser: open pages, click, type and read the page through its accessibility tree. It runs locally and is free.

**How do you add Playwright MCP to Claude Code?**

Run claude mcp add playwright -- npx @playwright/mcp@latest in your project, then start claude and approve the server if asked. Check it is connected with claude mcp list or /mcp inside Claude Code.

**Is Playwright MCP free?**

Yes. It is open source and runs on your own machine; you pay only for the model tokens Claude Code uses, which grow with each page snapshot the browser returns.


## Evidence

- Skryp's HTTP MCP endpoint worked from Claude Code 2.1.280 (added with claude mcp add --transport http, all 17 tools listed, scrape called and answered correctly) and from Codex CLI 0.159.0 (mcp_servers.skryp with url and bearer_token_env_var, scrape called and answered correctly) on 1 October 2026. (observed test, checked 1 Oct 2026; Claude Code and Codex CLI against the preview service; one task each. Limits: Cursor, Claude Desktop and VS Code were not tested. Codex CLI 0.155.1 could not run the account's configured model, so the run used the 0.159.0 CLI bundled with the ChatGPT app.)
- Claude Code's MCP documentation says tool search is on by default from version 2.1.221: MCP tool definitions are deferred until Claude searches for or calls a tool. It is off with ENABLE_TOOL_SEARCH=false or a custom ANTHROPIC_BASE_URL. MCP tool output is limited to 25,000 tokens by default, with a warning above 10,000. (provider docs, checked 1 Oct 2026; Claude Code, as documented. Limits: Defaults change between Claude Code versions.)
