Give Claude Code web access: Playwright MCP, a scraping MCP, or both
Updated 1 Oct 2026 · Tested on 1 Oct 2026
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.
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:
# 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[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:
# 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)'}")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. - 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 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[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 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.