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.
Quick Start: local stdio (recommended for Claude Desktop)
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+Qon 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 }plusmeta({ rung, solved, attempts, credits }, always present, whererungis one ofcache,probe,proxy,browser,fail) and, by default,session({ proxy, cookies, userAgent }) for replay through the lower-level tools. Nototal_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 includesexitCountryfoura_browser: distinct shape{ status, headers: object, body, cookies, userAgent }(note:bodymay 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
/mcprequests (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.