scrape-n-search MCP server
Unified web-search and scraping tools for AI agents, backed by multiple providers (TinyFish, Tavily, Exa, ZenRows, Scrappey, Firecrawl, Serper, Jina). The agent calls a handful of tools and never needs to know which provider served the request.
Because every provider fetches on its own servers, the client's IP (e.g. a VPN IP that target sites reject) is never seen by the target site.
Architecture
MCP client (agent)
│ MCP stdio protocol
▼
MCPServer (server.py) ── 7 tools: web_search, web_fetch, web_browser_actions,
web_screenshot, web_session, web_crawl, web_extract
│
▼
Router (router.py) ── picks provider by priority + capability, fails over on error
│
├── TinyFish (search, fetch)
├── Tavily (search, fetch)
├── Exa (search, fetch)
├── ZenRows (fetch, extract)
├── Scrappey (fetch, browser_actions, screenshot, session)
├── Firecrawl (search, fetch, crawl, extract)
├── Serper (search)
└── Jina (search*, fetch)
- Normalization: every adapter maps its provider-specific response into the shared
models (
SearchResult,FetchedPage,CrawlPage), so results look the same regardless of provider. - Selection: the router uses the first enabled provider (API key present) that supports the requested tool, in priority order.
- Failover: if the chosen provider errors, the router tries the next candidate and aggregates errors if all fail. The response always names the provider that served it.
- Default provider:
DEFAULT_PROVIDERforces a preferred provider for every call (the agent can still override per-call with theproviderparameter). When set, tools that provider cannot serve are hidden from the client.
Tools
Every tool returns a JSON string. All accept an optional provider parameter to force a
specific provider; otherwise the default/auto-selected provider is used.
web_search
Search the web and return ranked results.
| Param | Type | Default | Notes |
|---|---|---|---|
query |
string | — | required |
max_results |
int | 5 | 1–50 |
provider |
string? | null | force a provider |
freshness |
string? | null | hour | day | week | month | year (mapped per provider) |
country |
string? | null | geo-boost (Tavily, Firecrawl, Serper) |
Response: {query, provider, results: [{title, url, snippet, published_date?, score?}]}
web_fetch
Fetch a URL to clean content, server-side.
| Param | Type | Default | Notes |
|---|---|---|---|
url |
string | — | required |
format |
string | markdown |
markdown | html (enum) |
provider |
string? | null | force a provider |
js_render |
bool | false | render JS (Scrappey browser mode; ZenRows js_render) |
premium_proxy |
bool | false | residential proxy routing (Scrappey: always on; ZenRows premium_proxy) |
proxy_country |
string? | null | pin the Scrappey residential proxy region (e.g. UnitedStates); otherwise geo varies per request |
session |
string? | null | attach the request to a Scrappey session (see web_session; state does not persist across calls) |
Response: {provider, url, title?, format, content}
web_browser_actions
Run a sequence of browser actions on a URL (Scrappey full-browser mode) and return the
page content plus any execute_js results. Served by Scrappey only.
| Param | Type | Default | Notes |
|---|---|---|---|
url |
string | — | required |
actions |
array of objects | — | required — Scrappey action objects (click, type, scroll, execute_js, wait, wait_for_selector, goto, hover, keyboard, dropdown, …) |
format |
string | markdown |
markdown | html. Use html to receive execute_js results in javascript_return (the markdown filter suppresses them). |
provider |
string? | null | |
proxy_country |
string? | null | pin the Scrappey residential proxy region (e.g. UnitedStates) |
session |
string? | null | attach to a Scrappey session (see web_session) |
Response: {provider, url, format, content, javascript_return[]}
See the scrappey://playbook resource (or src/scrape_n_search/playbook.md) for
recipes — e.g. reading a full AliExpress item description (scroll + click "View more" +
extract).
web_screenshot
Capture a screenshot of a URL (Scrappey browser mode), returned as base64 PNG. Served by Scrappey only.
| Param | Type | Default |
|---|---|---|
url |
string | — |
full_page |
bool | false |
selector |
string? | null |
provider |
string? | null |
proxy_country |
string? | null |
session |
string? | null |
Response: {provider, url, format: "png", screenshot_base64}
web_session
Manage Scrappey browser-session lifecycle (Scrappey-only). Create a session, attach
requests to it via the session parameter of web_fetch / web_browser_actions /
web_screenshot, and destroy it when done.
| Param | Type | Default | Notes |
|---|---|---|---|
op |
string | — | create | destroy (enum) |
session |
string | — | required — session name |
provider |
string? | null |
Response: {provider, op, session, solution}
Known limitation (verified 2026-10): the create/destroy lifecycle works, but Scrappey currently destroys a named session once a
request.getcompletes, so cookies/browser state do not persist across calls yet despite the vendor docs. Treatsessionas a lifecycle-managed name; re-create sessions as needed.
web_crawl
Recursively crawl a domain. Served by Firecrawl only.
| Param | Type | Default |
|---|---|---|
url |
string | — |
max_pages |
int | 10 |
max_depth |
int | 2 |
provider |
string? | null |
Response: {provider, pages: [{url, title?, content?}]}
web_extract
Extract structured JSON from a page. Served by Firecrawl (prompt-based) and ZenRows
(extract=auto).
| Param | Type | Default |
|---|---|---|
url |
string | — |
prompt |
string? | null |
provider |
string? | null |
Response: {provider, url, data}
Provider coverage
| Provider | search | fetch | browser_actions | screenshot | session | crawl | extract | Requires |
|---|---|---|---|---|---|---|---|---|
| TinyFish | ✅ | ✅ | TINYFISH_API_KEY |
|||||
| Tavily | ✅ | ✅ | TAVILY_API_KEY |
|||||
| Exa | ✅ | ✅ | EXA_API_KEY |
|||||
| ZenRows | ✅ | ✅ | ZENROWS_API_KEY |
|||||
| Scrappey | ✅ | ✅ | ✅ | ✅ | SCRAPPEY_API_KEY |
|||
| Firecrawl | ✅ | ✅ | ✅ | ✅ | FIRECRAWL_API_KEY |
|||
| Serper | ✅ | SERPER_API_KEY |
||||||
| Jina | ✅* | ✅ | JINA_API_KEY for search; fetch is keyless |
browser_actions and screenshot are Scrappey-only capabilities (full-browser
features). * s.jina.ai (search) now requires an API key; r.jina.ai (fetch) still
works without one.
Configuration
API keys and settings live in mcp-server/.env (auto-loaded at startup; gitignored).
Copy .env.example to .env and fill in your keys. The server loads .env from the
working directory and falls back to the project's own mcp-server/.env, so it works
regardless of how it is launched. Existing environment variables take precedence over
.env.
| Var | Effect |
|---|---|
SCRAPPEY_API_KEY |
Enables the Scrappey provider (fetch). |
TINYFISH_API_KEY, TAVILY_API_KEY, EXA_API_KEY, ZENROWS_API_KEY, FIRECRAWL_API_KEY, SERPER_API_KEY, JINA_API_KEY |
Enable the corresponding providers. |
DEFAULT_PROVIDER |
Provider used when a call omits provider. When set, tools that provider cannot serve are hidden from the client. |
ENABLED_TOOLS |
Explicit comma-separated allow-list (web_search,web_fetch,web_browser_actions,web_screenshot,web_crawl,web_extract). Overrides the set derived from DEFAULT_PROVIDER. |
PROVIDER_PRIORITY |
Selection order for auto-picked providers (default: recommended stack order). |
HTTP_TIMEOUT |
HTTP timeout in seconds for provider API calls (default 60, connect 15). Scrappey browser requests take 20–40s — set 120 for long web_browser_actions sequences. |
CACHE_TTL |
TTL in seconds for cached web_fetch results (default 0 = off). Identical repeated fetches (same url/params/provider) are served from the router cache, so agents re-reading a page pay once. |
LOG_LEVEL |
Log verbosity: DEBUG, INFO (default), WARNING, ERROR, CRITICAL. INFO logs which provider served each tool call and failover attempts; DEBUG adds cache hits. |
JINA_KEYLESS |
Set 0 to disable the keyless Jina fallback. |
Example — Scrappey as the only/default provider (fetch + browser actions + screenshots):
# mcp-server/.env
SCRAPPEY_API_KEY=your_key_here
DEFAULT_PROVIDER=scrappey # hides web_search/web_crawl/web_extract
Scrappey
Scrappey is the cheapest scraping option in the research (€0.10/1K direct, €1/1K full
browser; pay-as-you-go, no subscription, balance never expires, residential proxies
included, pay-per-success). It provides fetch, browser actions, screenshots, and
session management (web_session) in this MCP.
Full documentation of its two modes, pricing, API, browser actions, and behavior:
docs/scrappey.md. A common-actions playbook for agents is
exposed as the scrappey://playbook MCP resource (source:
src/scrape_n_search/playbook.md).
Setup
python3 -m venv .venv
.venv/bin/pip install -e mcp-server
Run
.venv/bin/scrape-n-search # stdio MCP server
# or: .venv/bin/python -m scrape_n_search
MCP client config
Claude Desktop / Claude Code claude_desktop_config.json or .mcp.json:
{
"mcpServers": {
"scrape-n-search": {
"command": "/abs/path/to/scrape-n-search/.venv/bin/python",
"args": ["-m", "scrape_n_search"],
"cwd": "/abs/path/to/scrape-n-search/mcp-server",
"env": {
"SCRAPPEY_API_KEY": "..."
}
}
}
}
Oh My Pi (OMP) integration
Registered on this machine as a user-level MCP server in ~/.omp/agent/mcp.json
(scrape-n-search, stdio). After editing that file (or .env), reload in the OMP
session:
/mcp reload
/mcp list # confirm scrape-n-search is discovered
/mcp test scrape-n-search
The server auto-loads mcp-server/.env regardless of the working directory, so keys
added there apply without touching the OMP config.
Testing
.venv/bin/python -m pytest mcp-server/tests -q
Unit tests cover per-provider normalization (mocked HTTP), router selection/failover, and the MCP surface end-to-end over an in-memory transport.
Development — adding a provider
- Create
src/scrape_n_search/providers/<name>.pywith a subclass ofBaseProviderimplementing the tools it supports (seebase.pyand an existing adapter). Normalize responses into the shared models. - Register it in
src/scrape_n_search/providers/__init__.py(build_providers+DEFAULT_PRIORITY). - Add the env var to
.env.exampleand the coverage table above. - Add a normalization test to
tests/test_providers.py(mocked HTTP viahttpx.MockTransport).
Metadata
Release files for scrape-n-search-mcp 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| scrape_n_search_mcp-0.1.1.tar.gz | 33.2 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| scrape_n_search_mcp-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 62.5 kB
Release files / scrape_n_search_mcp-0.1.1.tar.gz
| Download URL | scrape_n_search_mcp-0.1.1.tar.gz |
|---|---|
| Size | 33.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
24dc81ca80dd737747e3632c2689df6744852733f9e22f3f9eebdeab88bab53f
|
|
BLAKE2b-256 checksum How to use checksums |
e7b2106760618167f07d6bbae0084a60c5f05f6a8b8d80254c19e5f1a8bf596f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.
Transparency logRelease files / scrape_n_search_mcp-0.1.1-py3-none-any.whl
| Download URL | scrape_n_search_mcp-0.1.1-py3-none-any.whl |
|---|---|
| Size | 29.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2ce886b443073fe9595eae0976d7de58aeaf702a88c05e26d52e2b1c289857e2
|
|
BLAKE2b-256 checksum How to use checksums |
b71c9b02887b6dd7efba63220d496823a9800272ecf0a6c50396ddcdb901cd1e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.
Transparency logSecurity for scrape-n-search-mcp 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for scrape_n_search_mcp-0.1.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.