Skip to content
Skryp

Web search MCP servers compared

Updated 1 Oct 2026 · Tested on 1 Oct 2026

A web search MCP server lets an agent search the web without leaving its tool loop. We ran the same 12 searches through four servers on 1 October 2026: Firecrawl's found the answer's site in the top five every time, Skryp's 11 times, open-websearch 9 and a DuckDuckGo server 6. Below: reply sizes, speed, price per 1,000 searches, setup, and the paid servers we did not test.

The same 12 searches through four servers

We started each server the way an agent does, as an MCP server, and called its search tool with the same 12 queries. Each query has a site that holds the answer: the official documentation, the standard, the bank, the store. We counted how often that site was among the first five pages in the reply. The script and what it printed:

web-search-mcp-servers/01-same-queries.py
# The same 12 searches through four web search MCP servers, called the way an agent calls them (MCP tools).
# Each query has a page a good result list should contain near the top; we count how often it is in the first 5.
# pip install mcp tiktoken    export SKRYP_API_KEY=... FIRECRAWL_API_KEY=...    (needs Node.js and uv)
# timeout: 900
import asyncio
import os
import re
import statistics
import time
from urllib.parse import urlsplit

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")

# (query, domains that hold the answer)
QUERIES = [
    ("python requests timeout", ("requests.readthedocs.io", "docs.python-requests.org")),
    ("playwright python installation", ("playwright.dev",)),
    ("model context protocol specification", ("modelcontextprotocol.io",)),
    ("beautifulsoup find_all", ("crummy.com",)),
    ("tokio spawn", ("docs.rs", "tokio.rs")),
    ("South African Reserve Bank repo rate", ("resbank.co.za",)),
    ("next.js app router documentation", ("nextjs.org",)),
    ("IANA time zone database", ("iana.org",)),
    ("firecrawl pricing", ("firecrawl.dev",)),
    ("portal 2 steam", ("store.steampowered.com",)),
    ("postgres jsonb index", ("postgresql.org",)),
    ("kubernetes horizontal pod autoscaler", ("kubernetes.io",)),
]

# name: (how to start it, search tool, arguments)
SERVERS = {
    "Skryp": ("http", "search", lambda q: {"query": q, "limit": 10}),
    "Firecrawl": (StdioServerParameters(command="npx", args=["-y", "firecrawl-mcp"],
                                        env={**os.environ, "FIRECRAWL_API_KEY": os.environ.get("FIRECRAWL_API_KEY", "")}),
                  "firecrawl_search", lambda q: {"query": q, "limit": 10}),
    "DuckDuckGo": (StdioServerParameters(command="uvx", args=["duckduckgo-mcp-server"]), "search",
                   lambda q: {"query": q, "max_results": 10}),
    "open-websearch": (StdioServerParameters(command="npx", args=["-y", "open-websearch@latest"], env={**os.environ, "MODE": "stdio"}),
                       "search", lambda q: {"query": q, "limit": 10}),
}

URL = re.compile(r"https?://[^\s\"'<>)\]]+")


def urls_in(text: str) -> list[str]:
    """Distinct pages in a reply, in order (a link to a section of a page counts as that page)."""
    out = []
    for u in URL.findall(text):
        u = u.rstrip(".,;:").split("#", 1)[0]
        if u not in out and "duckduckgo.com/l/" not in u:
            out.append(u)
    return out


def hit(urls: list[str], domains: tuple[str, ...]) -> bool:
    return any((urlsplit(u).hostname or "").removeprefix("www.").endswith(domains) for u in urls[:5])


async def run(session: ClientSession, tool: str, args) -> list[dict]:
    rows = []
    for q, domains in QUERIES:
        t0 = time.monotonic()
        try:
            r = await session.call_tool(tool, args(q))
            text = "\n".join(getattr(c, "text", "") for c in r.content)
            err = text[:80] if r.is_error else None
        except Exception as e:  # a failed call is a result too
            text, err = "", f"{type(e).__name__}"
        urls = urls_in(text)
        rows.append({"q": q, "ok": hit(urls, domains), "ms": (time.monotonic() - t0) * 1000, "n": len(urls),
                     "tokens": len(ENC.encode(text, disallowed_special=())), "err": err})
    return rows


async def one(name: str) -> list[dict]:
    how, tool, args = SERVERS[name]
    if how == "http":
        client = create_mcp_http_client(headers={"Authorization": f"Bearer {os.environ['SKRYP_API_KEY']}"})
        async with client, streamable_http_client(f"{API}/mcp", http_client=client) as (r, w):
            async with ClientSession(r, w) as s:
                await s.initialize()
                return await run(s, tool, args)
    async with stdio_client(how, errlog=open(os.devnull, "w")) as (r, w):
        async with ClientSession(r, w) as s:
            await s.initialize()
            return await run(s, tool, args)


async def main() -> None:
    results = {name: await one(name) for name in SERVERS}
    print(f"{'server':<16}{'answer in top 5':>16}{'median s':>10}{'pages':>7}{'tokens':>8}  errors")
    for name, rows in results.items():
        errs = sum(1 for r in rows if r["err"])
        print(f"{name:<16}{sum(r['ok'] for r in rows):>10} of {len(rows)}{statistics.median(r['ms'] for r in rows) / 1000:>10.1f}"
              f"{statistics.median(r['n'] for r in rows):>7.0f}{statistics.median(r['tokens'] for r in rows):>8.0f}  {errs}")
    print("\nper query (x = the answer's site was in the top 5)")
    names = list(results)
    print(f"{'':<38}" + "".join(f"{n[:10]:>11}" for n in names))
    for i, (q, _) in enumerate(QUERIES):
        print(f"{q:<38}" + "".join(f"{('x' if results[n][i]['ok'] else '-'):>11}" for n in names))


asyncio.run(main())
OutputRan 1 Oct 2026 · Python 3.12.14, tiktoken 0.14.0 · 112 s
server           answer in top 5  median s  pages  tokens  errors
Skryp                   11 of 12       3.5     10     730  0
Firecrawl               12 of 12       2.5     26    2866  0
DuckDuckGo               6 of 12       1.1     10     798  0
open-websearch           9 of 12       1.6     18    1364  0

per query (x = the answer's site was in the top 5)
                                            Skryp  Firecrawl DuckDuckGo open-webse
python requests timeout                         x          x          x          -
playwright python installation                  x          x          x          x
model context protocol specification            x          x          x          x
beautifulsoup find_all                          -          x          -          -
tokio spawn                                     x          x          x          x
South African Reserve Bank repo rate            x          x          x          x
next.js app router documentation                x          x          x          x
IANA time zone database                         x          x          -          x
firecrawl pricing                               x          x          -          x
portal 2 steam                                  x          x          -          x
postgres jsonb index                            x          x          -          -
kubernetes horizontal pod autoscaler            x          x          -          x

What the run shows[1 Oct 2026]:

  • Firecrawl's server found every answer, and Skryp's all but one (BeautifulSoup's documentation). open-websearch found 9 and the DuckDuckGo server 6.
  • Replies differ in size. Skryp's and the DuckDuckGo server's replies were about 750 tokens for 10 results. Firecrawl's were about 2,900, because each result carries a highlight from the page, with the links inside it. More text can save a follow-up read, but it fills the agent's context on every search.
  • Speed: the two open-source servers answered fastest, in about 1 to 2 seconds; Firecrawl's and Skryp's took 2.5 and 3.5 seconds at the median.

Twelve queries are a small sample, weighted towards documentation; run the script with your own queries before you choose.

What each one is

ServerHow it searchesPage content in the replyPrice per 1,000 searches
SkrypMerges several engines, through Skryp's routes; can search from a countrySnippets; scrape_results also reads the top results$0.20 to $0.50 (1 credit), depending on the plan; 5 credits from a residential IP in another country[1 Oct 2026]
FirecrawlFirecrawl's search APIHighlights from each page; full pages with scrape options2 credits per 10 results: $1.66 on Standard billed annually, $7.60 on Hobby billed monthly[1 Oct 2026]
DuckDuckGo server (Python package)Reads DuckDuckGo's result pagesSnippets; a separate tool fetches a pageFree; unofficial, 30 searches a minute by default
open-websearchReads result pages from search engines you chooseSnippets; separate tools fetch pagesFree; unofficial

Firecrawl's prices are its plan prices divided by its credits; Skryp's are its plans' prices per credit. The two free servers read search engines' public result pages, so they depend on those pages not changing or blocking them.

Connect one to Claude Code

bash
# Skryp (needs an API key)
claude mcp add --transport http skryp https://api.skryp.dev/mcp \
  --header "Authorization: Bearer $SKRYP_API_KEY"

# Firecrawl (needs an API key)
claude mcp add firecrawl -e FIRECRAWL_API_KEY=your-key -- npx -y firecrawl-mcp

# DuckDuckGo (no key; needs uv)
claude mcp add duckduckgo -- uvx duckduckgo-mcp-server

# open-websearch (no key; needs Node.js)
claude mcp add open-websearch -e MODE=stdio -- npx -y open-websearch@latest

These are the commands that started each server in our run, apart from the keys. Each server's tool definitions take up context once Claude Code loads them, so add only the one you need.

Servers that need an account

We did not test these three, because each needs an account; their prices are from their own pages on 1 October 2026:

ServerPackagePrice per 1,000 searchesFree each month
Brave Search@brave/brave-search-mcp-server$5[1 Oct 2026]$5 of credits
Exaexa-mcp-server$4 for Instant search, $7 for fast or auto[1 Oct 2026]$10 of credits
Tavilytavily-mcp$8 for a basic search, $16 for an advanced one[1 Oct 2026]1,000 credits

Unlike the two free servers above, all three are paid search APIs with published prices and limits.

Which to choose

If you wantUse
No key and no bill, for occasional searchesThe DuckDuckGo server or open-websearch, accepting that they depend on pages they read
The best hit rate in this run, with page highlights in each replyFirecrawl's server
Short replies and searching as seen from a country, with the same account as your page readsSkryp
A search API with a published price, without reading result pagesBrave Search, Exa or Tavily (not tested here)

See AI web scraping for what Skryp's other tools do with the results.

Questions

What is a web search MCP server?
A server that gives an AI agent a search tool over the Model Context Protocol, so the agent can search the web and read results without leaving its tool loop. You connect it once, for example with claude mcp add in Claude Code.
Which web search MCP server is free?
The DuckDuckGo server and open-websearch need no key or account: they read search engines' public result pages, so they depend on those pages. Brave gives $5 of credits a month, Exa $10 and Tavily 1,000 credits.
Which found the right page most often?
In our 12 searches on 1 October 2026, Firecrawl's server had the answer's site in the top five every time, Skryp's 11 times, open-websearch 9 and the DuckDuckGo server 6.
Does the search return page content?
Firecrawl's server returns highlights from each page. The others return snippets, with a separate tool or option to read whole pages; in Skryp, scrape_results reads the top results in the same call.

Evidence

  1. On 12 searches with a known answer, run through each server's MCP search tool on 1 October 2026, the answer's site was in the top 5 results 12 times with Firecrawl's server, 11 with Skryp's, 9 with open-websearch and 6 with the DuckDuckGo server. Median time per search was 2.5 s, 3.5 s, 1.6 s and 1.1 s; the median reply was 2,866, 730, 1,364 and 798 tokens.

    observed test · checked 1 Oct 2026 · 12 queries (programming documentation, standards, a central bank rate, a product page, a pricing page), 10 results asked for, each server called once per query from South Africa.

    Limits: 12 queries, one run, one location; the answer sites were chosen by us and favour documentation. Firecrawl's reply includes highlights with links from inside each result, which makes it longer. Results from engines that the open-source servers read change with those engines.

  2. Firecrawl's billing documentation prices search at 2 credits per 10 results, rounded up per 10 results; scraping the results adds each page's scrape cost; on the free plan, /search is limited to 10 requests a minute.

    provider docs · checked 1 Oct 2026 · Firecrawl search billing.

    Limits: Documentation, not a measured bill.

  3. 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).

  4. Brave's Search API page lists search at $5 per 1,000 requests with $5 of free credits every month and up to 50 queries a second.

    provider docs · checked 1 Oct 2026 · Brave Search API pricing.

    Limits: Not tested: needs an account.

  5. Exa's pricing page lists Instant search at $4 per 1,000 requests and fast or auto search at $7 per 1,000, $1 per 1,000 results beyond 10 per request, and $10 of free credits each month.

    provider docs · checked 1 Oct 2026 · Exa search pricing.

    Limits: Not tested: needs an account.

  6. Tavily gives 1,000 API credits a month free and charges $0.008 a credit pay as you go; a basic search costs 1 credit and an advanced search 2.

    provider docs · checked 1 Oct 2026 · Tavily search pricing.

    Limits: Not tested: needs an account.