Skip to main content
Glama
CommunityPokeOrg

remote-mcp-scraper-vercel

remote-mcp-scraper-vercel

A production-ready Remote Model Context Protocol (MCP) server for Vercel Serverless on Node.js, built with Next.js App Router and TypeScript.

It exposes scraping-oriented MCP tools over the legacy HTTP + SSE transport:

Endpoint

Purpose

GET /api/sse

Open an MCP session (text/event-stream, 15 s heartbeats)

POST /api/message?sessionId=…

Send JSON-RPC frames; responses stream back over SSE

GET /api/health

Liveness/status probe

Tools

Tool

Description

ping

Liveness probe — server name, version, uptime.

fetch_url

Fetch an http(s) URL with browser-like headers: rotating real User-Agent profiles, sec-ch-* client hints and Sec-Fetch-* spoofing, gzip/deflate/br decoding, bounded retries with exponential backoff + Retry-After honouring, header/body timeouts, redirect following, and a hard body-size cap.

take_screenshot

Render a page in headless Chromium and return a PNG/JPEG image plus navigation metadata (final URL, status, bytes, duration).

movie_metadata

Movie lookup from legitimate public sources only: TMDB (TMDB_API_KEY), OMDb (OMDB_API_KEY), or Wikipedia (no key required). No fabricated results — a miss returns a structured error.

All tool inputs are validated with strict zod schemas; outputs include structuredContent.

Related MCP server: Shark-no-Kari

Stack

  • Next.js 16 (App Router, Node.js runtime) — route handlers on Vercel Functions

  • @modelcontextprotocol/sdkMcpServer + SSEServerTransport, armSseKeepAlive heartbeats

  • puppeteer-core + @sparticuz/chromium — brotli-packed chrome-headless-shell sized for the ~50 MB serverless bundle budget

  • undicirequest API (full header control incl. Sec-*, unlike fetch) + ProxyAgent

  • socks — SOCKS4/5 CONNECT support via a custom undici connector (TLS-in-tunnel for HTTPS origins)

  • zod — input/output validation

  • vitest + eslint + tsc — validation (npm run validate)

Project layout

app/
  page.tsx                  minimal status page
  layout.tsx
  api/
    sse/route.ts            GET — opens MCP session over SSE
    message/route.ts        POST — JSON-RPC ingress for a session
    health/route.ts         liveness
lib/
  mcp.ts                    McpServer factory + tool registration
  sessions.ts               in-memory session registry + TTL sweep
  node-shims.ts             ServerResponse/IncomingMessage adapters over Web Streams
  fetcher.ts                stealth fetch client (UA rotation, proxies, retries, caps)
  user-agents.ts            rotating browser-identity profiles
  browser.ts                dynamic launcher: CDP → local binary → bundled chromium
  movie.ts                  TMDB / OMDb / Wikipedia metadata providers
  schemas.ts                zod input schemas
  auth.ts                   optional MCP_AUTH_TOKEN bearer gate
  rate-limit.ts             per-IP fixed-window limiter
tests/                      vitest unit tests
vercel.json                 function memory (1024 MB) + maxDuration + headers

Local development

npm install
cp .env.example .env   # fill in what you need (all optional for a first run)
npm run dev            # http://localhost:3000

Validate everything:

npm run validate       # eslint + tsc --noEmit + vitest + next build

Smoke-testing the MCP transport

# terminal 1 — open the SSE stream, note the sessionId in the `endpoint` event
curl -N http://localhost:3000/api/sse

# terminal 2 — drive the session
SID=<sessionId from the endpoint event>
curl -X POST "http://localhost:3000/api/message?sessionId=$SID" \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl","version":"0.0"}}}'
curl -X POST "http://localhost:3000/api/message?sessionId=$SID" \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","method":"notifications/initialized"}'
curl -X POST "http://localhost:3000/api/message?sessionId=$SID" \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"ping","arguments":{"message":"hi"}}}'

Responses arrive as event: message frames on the SSE stream.

MCP client configuration

Any client that supports the SSE transport can connect to https://<your-deployment>/api/sse. For mcp-remote-style bridges or Claude Desktop:

{
  "mcpServers": {
    "scraper": {
      "url": "https://<your-deployment>.vercel.app/api/sse",
      "headers": { "Authorization": "Bearer <MCP_AUTH_TOKEN>" }
    }
  }
}

Deployment (Vercel)

npm i -g vercel
vercel          # preview
vercel --prod   # production

vercel.json already configures 1024 MB function memory and a 60 s maxDuration for both API routes — required for the bundled Chromium binary. Set environment variables in the Vercel dashboard (or vercel env add).

Environment variables

Variable

Purpose

MCP_AUTH_TOKEN

Optional bearer token required on /api/sse + /api/message. Set this for any public deployment — unset means open access.

CDP_ENDPOINT

Remote Chrome DevTools endpoint (ws(s)://browserWSEndpoint, http(s)://browserURL). Highest-priority browser path — e.g. Browserless, Steel, self-hosted chrome --remote-debugging-port=9222.

PUPPETEER_EXECUTABLE_PATH

Local Chrome/Chromium binary for development (ignored on Vercel).

SCRAPER_PROXY_URL

Default upstream proxy for fetch_url / take_screenshot when the caller passes no proxy.

HTTP_PROXY / HTTPS_PROXY

Standard fallback proxy env vars for fetch_url.

TMDB_API_KEY

TMDB v3 key — primary movie_metadata provider.

OMDB_API_KEY

OMDb key — secondary movie_metadata provider.

RATE_LIMIT_MAX

Per-IP tool-call limit per window (0 disables; default 60).

RATE_LIMIT_WINDOW_MS

Rate-limit window (default 60000).

FETCH_MAX_BYTES

Response body cap in bytes (default 5 MiB).

FETCH_DEFAULT_TIMEOUT_MS

Per-request timeout (default 20000).

SESSION_TTL_MS

Idle-session eviction (default 600000).

Proxy configuration

fetch_url and take_screenshot accept a proxy argument and honour the env vars above. Supported schemes:

  • http:// / https:// — via undici ProxyAgent (CONNECT tunnelling, optional auth in the URL).

  • socks5://, socks5h://, socks4://, socks4a:// — via socks + a custom undici connector; HTTPS origins get TLS inside the tunnel. Credentials may be embedded (socks5://user:pass@host:1080).

  • For take_screenshot, the proxy is passed to Chromium as --proxy-server and embedded credentials are applied with page.authenticate().

Proxy credentials are redacted from all error messages.

Security & privacy

  • Auth: optional MCP_AUTH_TOKEN bearer (header or ?token=); no auth is otherwise enforced — treat an unauthenticated deployment as public tooling.

  • Rate limiting: fixed-window per-IP limiter on tool calls (instance-local, best-effort).

  • Allowlisting: none — the tools fetch arbitrary http(s) URLs. If you expose this publicly, set MCP_AUTH_TOKEN and consider fronting it with your own allowlist; see limitations for SSRF context.

  • No credential leakage: proxy userinfo is stripped from URLs used in errors, logs, and browser flags.

  • Data handling: no persistence — request bodies/screenshots live only for the duration of a call; sessions live in instance memory.

Responsible use

fetch_url makes requests look like ordinary browser traffic to avoid incidental breakage — it is not a bypass for authentication, authorization, paywalls, CAPTCHAs, or site rate-limit policy, and must not be used as one. Honour robots.txt, the target site's terms of service, and applicable law. The MCP_AUTH_TOKEN gate and rate limiter are abuse dampers, not access control for the sites you scrape.

Known limitations

  • Session affinity: the in-memory session map lives inside one function instance. POST /api/message must reach the same warm instance as GET /api/sse; a cold start or scale-out returns 404 unknown_session. For multi-instance deployments, switch to the MCP Streamable HTTP transport (StreamableHTTPServerTransport) with an external session store (e.g. Redis), or keep traffic single-instance.

  • Function lifetime: SSE connections die at maxDuration (60 s default); MCP clients should reconnect and re-initialize.

  • Cold starts: the first take_screenshot on a cold instance extracts ~50 MB of brotli-packed Chromium — expect a few seconds; warm calls are sub-second.

  • Binary ceiling: @sparticuz/chromium ships chrome-headless-shell — no GPU rendering, no PDF printing; setGraphicsMode is disabled.

  • Rate limiter is per-instance and memory-resident (not distributed).

  • Node ≥ 20.9 required (Vercel nodejs20.x+ runtime).

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables browser automation and web scraping by exposing Playwright tools through an HTTP-based MCP server. Users can navigate pages, interact with web elements, capture screenshots, and extract structured content using a persistent Chromium instance.
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Remote MCP server for web scraping with anti-bot evasion. Provides stealth HTTP fetching, headless browser with Cloudflare bypass, CSS selectors, YouTube transcripts, and Markdown conversion.
    1
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    MCP server to scrape web pages to clean Markdown via headless Chromium, with support for single or batch URLs.
    -