MCP Server
MCP Server
Use FourA from any Model Context Protocol client (Claude Desktop, Claude Code, Cursor, Windsurf, VS Code) as three native tools and five workflow prompts. No integration code, no custom HTTP client.
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 (~10s) 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_single, foura_proxy, foura_browser) and five 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_..."
}
}
}
}
Current Claude Desktop builds reject the bare url form. Use the stdio config above for Claude Desktop, or bridge 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 three 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 three 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_single
One HTTP request, response back. 200ms to 2s. Mirrors POST /api/single/ one-to-one.
Use for static pages, JSON APIs, server-rendered HTML.
foura_proxy
HTTP request routed through a rotating proxy pool with automatic retry. 1 to 5s. Mirrors POST /api/proxy/.
Use when foura_single returns 403, captcha, or geo-blocked content. The response includes the proxy ID that succeeded (proxy) and the outer wall-clock time in seconds (total, float).
foura_browser
Full browser session. JavaScript runs, the DOM renders, cookies come back. 2 to 10s. 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_single:{ status, headers, data, total_time, ... }(headers is an array, one entry per redirect hop)foura_proxy: same as single plus{ proxy, total }(wheretotalis float seconds, outer timing including retries)foura_browser: distinct shape{ status, headers: object, body, cookies, userAgent }(note:bodymay be a string or object depending on content-type)
Clients that support structuredContent (Claude Desktop, Cursor, Windsurf as of 2026) pass the typed object directly to the LLM, skipping JSON-as-string re-tokenization. Expect 30-40% token savings on typical responses.
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
Five 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 |
|---|---|---|
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": "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 |
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
Each instance runs in one container statelessly. A public GitHub mirror lands with v1.0. Until then the source is in a private repo and the container image is available on request. Contact support@foura.ai for early access.
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 |
Runs as uid 1001 (non-root) in the official container. 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.