Skip to content
Skryp

The best MCP servers for Claude Code, tested

Updated 1 Oct 2026 · Tested on 1 Oct 2026

For web access in Claude Code: Playwright MCP to drive a browser, Fetch for the odd page, Firecrawl or Skryp for pages as compact Markdown, and Context7 or DeepWiki for documentation. We started seven servers as Claude Code does and ran each on the tasks it is for; below are the results, the size of each server's tool definitions, setup commands and costs. Skryp is ours.

Seven servers, run on the tasks they are for

We started each server with the command Claude Code uses and called it through the official MCP client on three tasks: read a documentation page, read a page built by JavaScript, and answer a question about a library. Page readers ran the first two; documentation servers the third. We also counted how many tokens each server's tool definitions take up once they are loaded. The script and what it printed on 1 October 2026:

best-mcp-servers-claude-code/01-servers-on-tasks.py
# Seven MCP servers you might add to Claude Code, started the way Claude Code starts them and run on the tasks
# each is for: (A) read a documentation page, (B) read a page built by JavaScript, (C) answer a library question.
# Also measured: how many tokens each server's tool definitions add to every request Claude Code makes.
# pip install mcp tiktoken    export SKRYP_API_KEY=... FIRECRAWL_API_KEY=...    (needs Node.js and uv)
# timeout: 900
import asyncio
import json
import os
import re
import time
from contextlib import AsyncExitStack

import tiktoken
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from mcp.client.streamable_http import create_mcp_http_client, streamable_http_client

API = os.environ.get("SKRYP_API_URL", "https://api.skryp.dev")
ENC = tiktoken.get_encoding("cl100k_base")
DOCS = "https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/404"
JS = "https://quotes.toscrape.com/js/"
CHECK = {  # text that must be in the reply for the task to count as done
    "A": ["cannot find the requested resource"],
    "B": ["The world as we have created it", "It is our choices, Harry", "only two ways to live your life"],
    "C": ["cleanup"],
}


def text_of(result) -> str:
    return "\n".join(getattr(c, "text", "") for c in result.content)


async def page_reader(s, tool, args):  # one call that returns the page
    return text_of(await s.call_tool(tool, args))


async def playwright(s, url):
    await s.call_tool("browser_navigate", {"url": url})
    await asyncio.sleep(1)
    return text_of(await s.call_tool("browser_snapshot", {}))


async def devtools(s, url):
    pages = text_of(await s.call_tool("new_page", {"url": url}))  # "2: Quotes to Scrape (https://…) [selected]"
    page_id = int(re.search(r"(\d+):[^\n]*\[selected\]", pages).group(1))
    return text_of(await s.call_tool("take_snapshot", {"pageId": page_id}))


async def context7(s, _):
    found = text_of(await s.call_tool("resolve-library-id", {"libraryName": "react", "query": "useEffect cleanup"}))
    lib = re.search(r"(/[\w.-]+/[\w.-]+)", found).group(1)
    return text_of(await s.call_tool("query-docs", {"libraryId": lib, "query": "What does the useEffect cleanup function do?"}))


async def deepwiki(s, _):
    return text_of(await s.call_tool("ask_wiki_question", {"repoName": "facebook/react",
                                                          "question": "What does the cleanup function returned from useEffect do?"}))


def stdio(cmd, *args, env=None):
    return StdioServerParameters(command=cmd, args=list(args), env={**os.environ, **(env or {})})


# name: (how to start it, {task: how to run it})
SERVERS = {
    "Playwright MCP": (stdio("npx", "-y", "@playwright/mcp@latest", "--headless"), {"A": playwright, "B": playwright}),
    "Chrome DevTools MCP": (stdio("npx", "-y", "chrome-devtools-mcp@latest", "--headless", "--isolated"), {"A": devtools, "B": devtools}),
    "Fetch": (stdio("uvx", "mcp-server-fetch"), {t: (lambda s, u: page_reader(s, "fetch", {"url": u, "max_length": 20000})) for t in "AB"}),
    "Firecrawl": (stdio("npx", "-y", "firecrawl-mcp", env={"FIRECRAWL_API_KEY": os.environ.get("FIRECRAWL_API_KEY", "")}),
                  {t: (lambda s, u: page_reader(s, "firecrawl_scrape", {"url": u, "formats": ["markdown"]})) for t in "AB"}),
    "Skryp (ours)": (("http", f"{API}/mcp", {"Authorization": f"Bearer {os.environ.get('SKRYP_API_KEY', '')}"}),
                     {t: (lambda s, u: page_reader(s, "scrape", {"url": u})) for t in "AB"}),
    "Context7": (stdio("npx", "-y", "@upstash/context7-mcp"), {"C": context7}),
    "DeepWiki": (("http", "https://mcp.deepwiki.com/mcp", {}), {"C": deepwiki}),
}


async def run(name):
    how, tasks = SERVERS[name]
    row = {"name": name}
    async with AsyncExitStack() as stack:
        if isinstance(how, tuple):  # ("http", url, headers)
            client = await stack.enter_async_context(create_mcp_http_client(headers=how[2]))
            r, w = await stack.enter_async_context(streamable_http_client(how[1], http_client=client))
        else:
            r, w = await stack.enter_async_context(stdio_client(how, errlog=open(os.devnull, "w")))
        async with ClientSession(r, w) as s:
            await s.initialize()
            tools = (await s.list_tools()).tools
            defs = [{"name": t.name, "description": t.description, "input_schema": t.input_schema} for t in tools]
            row["tools"], row["def_tokens"] = len(tools), len(ENC.encode(json.dumps(defs), disallowed_special=()))
            for task, url in (("A", DOCS), ("B", JS), ("C", None)):
                if task not in tasks:
                    continue
                t0 = time.monotonic()
                try:
                    out = await tasks[task](s, url)
                except Exception as e:
                    out = f"error: {type(e).__name__}"
                ok = all(c.lower() in out.lower() for c in CHECK[task])
                row[task] = (ok, time.monotonic() - t0, len(ENC.encode(out, disallowed_special=())))
    return row


async def main():
    rows = [await run(n) for n in SERVERS]
    print(f"{'server':<22}{'tools':>6}{'definition tokens':>19}   {'A: docs page':<20}{'B: JavaScript page':<20}{'C: library question':<20}")
    for r in rows:
        cells = []
        for t, width in (("A", 20), ("B", 20), ("C", 20)):
            if t in r:
                ok, secs, tok = r[t]
                cells.append(f"{'done' if ok else 'missed'} {secs:.0f}s {tok:,}t".ljust(width))
            else:
                cells.append("-".ljust(width))
        print(f"{r['name']:<22}{r['tools']:>6}{r['def_tokens']:>19,}   {''.join(cells)}")
    print("\nper task: done or missed, seconds, tokens in the reply (t)")


asyncio.run(main())
OutputRan 1 Oct 2026 · Python 3.12.14, tiktoken 0.14.0 · 71 s
server                 tools  definition tokens   A: docs page        B: JavaScript page  C: library question 
Playwright MCP            25              4,445   done 1s 9,843t      done 2s 1,123t      -                   
Chrome DevTools MCP       30              6,505   done 3s 8,986t      done 4s 1,507t      -                   
Fetch                      1                290   done 2s 781t        missed 2s 28t       -                   
Firecrawl                 27             14,511   done 1s 1,997t      done 0s 564t        -                   
Skryp (ours)              17              6,319   done 0s 1,377t      done 0s 419t        -                   
Context7                   2              1,036   -                   -                   done 4s 1,413t      
DeepWiki                   3                286   -                   -                   done 43s 534t       

per task: done or missed, seconds, tokens in the reply (t)

What the run shows[1 Oct 2026]:

  • Browsers return big pages. Playwright MCP and Chrome DevTools MCP read the documentation page as an accessibility snapshot of about 9,000 to 10,000 tokens; Firecrawl and Skryp returned it as 1,400 to 2,000 tokens of Markdown. Claude Code warns when an MCP result passes 10,000 tokens and cuts it at 25,000 by default[1 Oct 2026].
  • Fetch does not run JavaScript. It read the documentation page but not the quotes, which a script adds after the page loads.
  • Tool definitions differ fifty-fold. Firecrawl's 27 tools come to 14,511 tokens; Fetch's one tool to 290. Skryp's 17 tools are 6,319 tokens, more than Playwright MCP's 25.
  • Documentation servers answer from the source. Context7 and DeepWiki both answered the React question; DeepWiki took 43 seconds.

Claude Code defers MCP tool definitions by default and loads a server's tools when Claude looks for them[1 Oct 2026], so a large server costs context mainly when it is used. With tool search off, or in clients that load every tool up front, each server's definitions go with every request.

The servers

Playwright MCP: a browser Claude drives

bash
claude mcp add playwright -- npx @playwright/mcp@latest

A local browser that Claude navigates, clicks and types in, step by step. Free; needs Node.js. Best for testing your own app and multi-step flows; its page snapshots are large for reading. See Claude Code web access for Claude Code sessions with it.

Chrome DevTools MCP: debugging and performance

bash
claude mcp add chrome-devtools -- npx chrome-devtools-mcp@latest

Drives Chrome through its DevTools, with tools for console messages, network requests, performance traces and Lighthouse audits. Free; needs Node.js and Chrome. Best for debugging pages you are building rather than reading other sites.

Fetch: the smallest page reader

bash
claude mcp add fetch -- uvx mcp-server-fetch

One tool that fetches a URL and returns it as Markdown. Free; needs uv. It reads what the server sends, without JavaScript, from your own connection.

Firecrawl: pages, crawls and search through an API

bash
claude mcp add firecrawl -e FIRECRAWL_API_KEY=your-key -- npx -y firecrawl-mcp

Scrape, crawl, map, search and more through Firecrawl's API. Needs an account: 1,000 free credits a month, plans from $16 a month billed annually[1 Oct 2026]. Its tool definitions are the largest here. See Skryp vs Firecrawl.

Skryp: pages and data from the country you choose (ours)

bash
claude mcp add --transport http skryp https://api.skryp.dev/mcp \
  --header "Authorization: Bearer $SKRYP_API_KEY"

Reads pages as Markdown or checked JSON, searches, crawls, keeps datasets and monitors, and drives a live browser, with the exit country verified and the cost on every result. Needs an account: 1,000 free credits a month, plans from $9[1 Oct 2026]. See AI web scraping.

Context7: current library documentation

bash
claude mcp add context7 -- npx -y @upstash/context7-mcp

Looks up a library and returns passages and code examples from its documentation for your question. Two small tools.

DeepWiki: questions about a GitHub repository

bash
claude mcp add --transport http deepwiki https://mcp.deepwiki.com/mcp

Answers questions about a public GitHub repository from a generated wiki of it. No install; three small tools. It was the slowest to answer in our run.

Which to add

ForAdd
Testing your own app in a browserPlaywright MCP
Debugging and profiling pagesChrome DevTools MCP
Reading the odd public page, at no costFetch
Reading pages, including JavaScript-built and blocked ones, as compact MarkdownFirecrawl or Skryp
Pages as seen from a country, with the exit verifiedSkryp
Up-to-date documentation for the libraries you useContext7
Questions about a GitHub codebaseDeepWiki

Add servers to the projects that need them (claude mcp add --scope project) and check what is connected with /mcp.

Questions

What are the best MCP servers for Claude Code?
It depends on the job: Playwright MCP to drive a browser, Chrome DevTools MCP to debug pages, Fetch for the odd public page, Firecrawl or Skryp to read pages as compact Markdown, and Context7 or DeepWiki for documentation.
Do MCP servers use up Claude Code's context?
Their tool definitions do once loaded: from 286 tokens for DeepWiki to 14,511 for Firecrawl in our run. Claude Code defers MCP tool definitions by default and loads a server's tools when Claude looks for them; their results count too, and Claude Code warns above 10,000 tokens.
Which servers are free?
Playwright MCP, Chrome DevTools MCP and Fetch run on your machine at no cost; Context7 and DeepWiki answered our questions without an account. Firecrawl and Skryp need accounts, each with 1,000 free credits a month.
How do I add an MCP server to Claude Code?
With claude mcp add, for example claude mcp add fetch -- uvx mcp-server-fetch, or with --transport http for a hosted server. Add --scope project to keep it to one project, and check connections with /mcp.

Evidence

  1. Started as Claude Code starts them and run on 1 October 2026, the tool definitions of Firecrawl's MCP server (27 tools) came to 14,511 tokens, Chrome DevTools MCP (30 tools) 6,505, Skryp (17) 6,319, Playwright MCP (25) 4,445, Context7 (2) 1,036, Fetch (1) 290 and DeepWiki (3) 286. Playwright MCP, Chrome DevTools MCP, Firecrawl and Skryp read both a documentation page and a page built by JavaScript; Fetch read only the documentation page. Reading the documentation page, Playwright MCP's and Chrome DevTools MCP's page snapshots were 9,843 and 8,986 tokens, against Firecrawl's 1,997 and Skryp's 1,377 tokens of Markdown. Context7 and DeepWiki both answered a React question; DeepWiki took 43 seconds.

    observed test · checked 1 Oct 2026 · Seven servers, each run once on the tasks it is for: an MDN reference page, quotes.toscrape.com/js, and a question about React's useEffect.

    Limits: One run, one location, simple tasks. Skryp's and Firecrawl's quick times include results they had cached from earlier runs. Claude Code itself did not drive these calls; its own choices between servers are recorded in the Claude Code web access guide.

  2. 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.

  3. Firecrawl's pricing page (prices effective 4 September 2026) lists Free (1,000 credits a month), Hobby ($19 a month, or $16 billed annually, 5,000 credits), Standard ($83 a month billed annually, 100,000 credits), Growth ($333, 500,000) and Scale ($599, 1,000,000). One credit is one page on a basic scrape, crawl or map; a page that responds with an error status such as 403 or 404 costs 1 credit, and a scrape that returns no result is not charged.

    provider docs · checked 1 Oct 2026 · firecrawl.dev/pricing

    Limits: Prices change; re-read on the day a page citing them is published.

  4. Skryp's plans from 1 October 2026: Free (1,000 credits a month), Hobby ($9 a month for 20,000 credits), Standard ($49 for 150,000), Growth ($149 for 600,000) and Scale ($399 for 2,000,000); unused plan credits expire at the end of each month, and top-ups, which never expire (also after a plan is cancelled), cost $10 for 20,000 credits on Free and the plan's rate on a plan. A page costs 1 credit through Skryp's own network, 5 through a residential or mobile proxy (10 with JavaScript rendering) and 25 for advanced unblocking, only when it succeeds; model extraction adds 5; failed, blocked and cached results cost 0, and a missing page (404 or 410) costs at most 1.

    observed test · checked 1 Oct 2026 · Skryp's own prices (the service's rate card).

    Limits: Prices in US dollars, charged in rand through Paystack at R17 to the US dollar (Chad, 2 Oct 2026).