MCP Server

MCP Server

Use FourA from any Model Context Protocol client (Claude Desktop, Claude Code, Cursor, Windsurf, VS Code) as four native tools and six workflow prompts. No integration code, no custom HTTP client.

Open source on GitHub; on npm as @fouradata/mcp. Current release: 0.5.0.

Grab a key at foura.ai/dashboard#api-keys (one click, shown once on creation, format pk_live_...). Drop this into your MCP client's config:

{
  "mcpServers": {
    "foura": {
      "command": "npx",
      "args": ["-y", "@fouradata/mcp"],
      "env": { "FOURA_API_KEY": "pk_live_..." }
    }
  }
}

Claude Desktop gotcha: fully quit Claude Desktop (Cmd+Q on macOS) before editing the config file. If the app is still running, it will overwrite your edits with its in-memory config on exit.

The npx command downloads @fouradata/mcp on first launch and runs it as a subprocess of your MCP client. No global install needed.

Client Where the config lives
Claude Desktop (macOS) ~/Library/Application Support/Claude/claude_desktop_config.json
Claude Desktop (Windows) %APPDATA%\Claude\claude_desktop_config.json
Claude Code claude mcp add foura -- npx -y @fouradata/mcp (set FOURA_API_KEY in env first)
Cursor ~/.cursor/mcp.json
Windsurf ~/.codeium/windsurf/mcp_config.json
VS Code (MCP extension) .vscode/mcp.json

Restart the client. The tools (foura_auto, foura_single, foura_proxy, foura_browser) and six prompts appear in your tool list.

Quick Start: hosted (Streamable HTTP)

For clients that support the Streamable HTTP transport (Cursor, Windsurf, VS Code, Claude Code with --transport http), point them at the hosted endpoint instead of running a local subprocess:

{
  "mcpServers": {
    "foura": {
      "url": "https://mcp.foura.ai/mcp",
      "headers": {
        "Authorization": "Bearer pk_live_..."
      }
    }
  }
}

For Claude Desktop, use the stdio config above or bridge the hosted endpoint through mcp-remote:

{
  "mcpServers": {
    "foura": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://mcp.foura.ai/mcp", "--header", "Authorization: Bearer pk_live_..."]
    }
  }
}

Hosted endpoint reference

Property Value
URL https://mcp.foura.ai/mcp
Transport Streamable HTTP (POST /mcp, SSE responses)
Authentication Authorization: Bearer pk_live_... per request
MCP-Protocol-Version Per @modelcontextprotocol/sdk (currently 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05, 2024-10-07)
401 challenge WWW-Authenticate: Bearer realm="foura-mcp", resource_metadata="https://foura.ai/docs/mcp/server#auth"

The hosted server is stateless. Every request brings its own key, which the server forwards to the FourA API as X-API-Key. One key opens all four tools.

For protection against DNS-rebinding (CVE-2025-66414), the server validates the Host header (must be mcp.foura.ai or localhost) and the Origin header when present (allowlisted: mcp.foura.ai, claude.ai, app.cursor.sh, app.cursor.com). Server-to-server callers (curl, MCP clients in stdio bridge mode) send no Origin and pass through.

Tools

All four tools are annotated readOnlyHint: true and openWorldHint: true per the MCP 2025-06-18 spec. Clients that auto-approve trusted read-only tools call them without a per-request confirmation modal.

foura_auto is the smart default: give it a URL and it returns the content, picking the fetch method for you. The other three are the lower-level primitives it orchestrates; reach for them when you want explicit control.

foura_auto

Give it a URL when you want FourA to choose the request method. It makes bounded attempts across the available HTTP, proxy, and browser paths. Pass validate on protected targets so the response must contain content that identifies the real page. If no attempt satisfies the validation, the tool returns an error instead of presenting a challenge page as success.

The response includes completion details in meta and, by default, a reusable session with proxy, cookies, and userAgent. For a plain follow-up, call foura_single with session.proxy as proxy, serialize the cookies as a Cookie header, and send session.userAgent as the User-Agent header. For JavaScript rendering, pass the session values to the matching foura_browser fields.

foura_single

One HTTP request, response back. Mirrors POST /api/single/ one-to-one.

Use for static pages, JSON APIs, server-rendered HTML.

foura_proxy

Route one HTTP request through rotating proxies with automatic retry. Use it when foura_single is blocked or the target requires a specific exit country.

Set exitCountries to a strict allowlist of two-letter target-visible country codes supplied by the user or target requirements:

{
  "maxTries": 5,
  "exitCountries": ["CZ", "GB"],
  "request": {
    "method": "GET",
    "url": "https://example.com/pricing",
    "unblocker": true
  }
}

Values are trimmed, uppercased, and deduplicated. Proxies with unknown exits are excluded, and the request never falls back to an unrequested country. Selection uses the latest available target-visible country metadata, normally updated within ten minutes; it is not a live geolocation lookup during the request. Do not infer the serving country from the proxy host address.

A scoped success returns exitCountry and the reusable proxy ID. Check that exitCountry belongs to the requested allowlist. If the current pool has no match, the tool returns code: "no_eligible_proxy" with the normalized scope in details.exitCountries. Preserve that scope and retry later. Change or widen it only when the user explicitly changes the requirement.

If the selected page later needs JavaScript, pass the returned proxy ID to foura_browser.proxy so the browser reuses the same exit.

foura_browser

Full browser session. JavaScript runs, the DOM renders, cookies come back. Mirrors POST /api/browser/.

Use for single-page apps, lazy-loaded content, or pages behind anti-bot challenges that need a real browser to clear.

For input shapes, defaults, and validation rules on each tool, refer to the REST endpoint reference. Tool schemas match the REST API field-for-field, plus the MCP-only offload_large opt-in (see below).

Typed responses

Every tool response includes both content (human-readable text summary) and structuredContent (typed JSON validated against the tool's outputSchema). Each tool has its own unique shape:

  • foura_auto: single-shaped { status, headers, data } plus meta ({ rung, solved, attempts, credits }, always present, where rung is one of cache, probe, proxy, browser, fail) and, by default, session ({ proxy, cookies, userAgent }) for replay through the lower-level tools. No total_time.
  • foura_single: { status, headers, data, total_time, ... } (headers is an array, one entry per redirect hop)
  • foura_proxy: same as single plus { proxy, total }; a scoped success also includes exitCountry
  • foura_browser: distinct shape { status, headers: object, body, cookies, userAgent } (note: body may be a string or object depending on content-type)

Clients that support structuredContent can pass the typed object directly to the LLM instead of requiring it to parse JSON from prose.

Multi-value response headers

Headers that appear multiple times (Set-Cookie, Link, WWW-Authenticate) come back as arrays:

{
  "headers": [
    {
      "result": { "version": "HTTP/2", "code": 200, "reason": "" },
      "content-type": "text/html",
      "set-cookie": ["a=1; Path=/", "b=2; Path=/"]
    }
  ]
}

This matters for sites that set session + tracking + consent cookies in one response (most of e-commerce).

Large responses: offload_large (default: inline)

By default (since v0.2.0), full response bodies are returned inline in structuredContent regardless of size. This works in every MCP client out of the box.

If your client supports MCP resources/read AND you want token savings on big pages, pass offload_large: true per tool call. Responses >= 50 KB are then written to disk, returned as a resource_link, and your client fetches the body only when it actually needs it. Cached payloads expire after 1 hour.

{
  "method": "GET",
  "url": "https://en.wikipedia.org/wiki/Web_scraping",
  "offload_large": true
}
Client offload_large: true
Claude Desktop not yet, leave default false
Claude Code, Cursor, Windsurf supported
VS Code MCP extension supported

Tenant-isolated: each API key gets its own namespace (sha256(apiKey)[:16]). Only the key that stored a payload can read it back. Cross-tenant reads return Payload not found with no existence leak.

Built-in Prompts

Six workflow templates surface under /prompts in any MCP client. Each takes named arguments and returns a templated user message orchestrating one or more tools.

Prompt Arguments What it does
smart_fetch url, optional must_contain, extract Auto fetch (picks the method, handles bot protection), then return or extract the content
scrape_product_page url Browser fetch, then extract product title, price, image, stock, SKU as JSON
extract_article url Single with proxy fallback, then strip nav/ads and return clean article JSON
monitor_pricing url, optional target_price Proxy fetch, extract current price, compare to target
check_endpoint_health url, optional expected_text Single with strict validation, return reachability and timing
bulk_fetch_urls urls (comma-separated) Parallel single, auto-fallback to proxy per URL, return metadata only

Prompts cost zero tokens at idle. Only invoked prompts enter the LLM context.

Full text plus manual fallback prompts: MCP Recipes.

Error envelope

Every error (isError: true) carries a structuredContent envelope. Minimum fields on every error:

{
  "service": "auto | single | proxy | browser",
  "code": "rate_limited",
  "error": "Rate limit exceeded"
}

On upstream errors with HTTP status, status is also present. On rate-limit and capacity errors, the upstream envelope adds retryAfter, current.{concurrency, rpm}, and limits.{maxConcurrency, maxRpm}. See API Errors for the underlying REST shape.

Stable code values:

Code HTTP Meaning Retry safe?
ssrf_blocked n/a Target IP in a private or reserved range (RFC 5735, 6598, IPv6 reserved) No, change URL
upstream_non_json varies Upstream returned malformed body Maybe, investigate
output_validation_failed n/a The MCP server's outputSchema rejected the upstream response (server bug or unexpected upstream shape) Maybe, report
bad_request 400 Input shape rejected No, fix arguments
auth_failed 401 Key missing, invalid, or deactivated No, fix the key
forbidden 403 Authenticated but not allowed No, or switch to foura_proxy
not_found 404 Target or endpoint missing No
rate_limited 429 RPM cap hit Yes, wait retryAfter
at_capacity 503 Concurrency cap hit Yes, wait retryAfter
service_disabled 503 Maintenance window or your plan doesn't include this tool Contact support
service_unavailable 503 Generic 503 Yes, short backoff
upstream_error 500+ Upstream 5xx Yes, exponential backoff
upstream_client_error 4xx Other 4xx Usually no
upstream_unknown other Defensive, should not occur in practice Investigate
no_eligible_proxy n/a No proxy matches the strict exitCountries scope Retry later; change scope only explicitly

LLM agents can read code directly for retry logic without parsing prose. Authentication walkthrough: Authentication.

Limits

  • Inline body by default. With offload_large: true, responses >= 50 KB go to disk + resource_link (per-tenant, 1-hour TTL).
  • Private targets are refused (RFC 5735, RFC 6598, IPv6 reserved blocks) at the MCP layer. Only public hosts forwarded.
  • Request body cap of 256 KB on incoming /mcp requests (real MCP payloads are < 4 KB).
  • Rate limits are enforced by the FourA API per service. See Rate Limits.

Self-Hosting

The full server source is public on GitHub under @fouradata/mcp. Clone the repo, npm install, npm run build, and run node dist/http.js to stand up your own instance. Runs stateless in a single container behind any load balancer.

Configurable environment:

Variable Default Purpose
PORT 3076 HTTP listen port
FOURA_API_BASE https://api.foura.ai/api Upstream FourA REST base URL
FOURA_MCP_PAYLOADS_DIR /data/payloads Where >= 50 KB responses are cached on disk (with offload_large: true)
FOURA_MCP_ALLOWED_HOSTS mcp.foura.ai,localhost,127.0.0.1,[::1] Hostname allowlist for Host header (DNS-rebinding defense)
FOURA_MCP_ALLOWED_ORIGINS https://mcp.foura.ai,https://claude.ai,https://app.cursor.sh,https://app.cursor.com Origin allowlist for browser callers
FOURA_MCP_RESOURCE_METADATA_URL https://foura.ai/docs/mcp/server#auth URL returned in WWW-Authenticate on 401

The official container runs as uid 1001 (non-root). The /data/payloads host bind mount must be writable by that uid.

Scale horizontally behind any load balancer. Clients supply their key on every request, so there's no sticky session.

Last updated: July 13, 2026