Servidor MCP
Servidor MCP
Usa FourA desde cualquier cliente de Model Context Protocol (Claude Desktop, Claude Code, Cursor, Windsurf, VS Code) como cuatro herramientas nativas y seis prompts de flujo de trabajo. Sin código de integración ni clientes HTTP personalizados.
Código abierto en GitHub; en npm como @fouradata/mcp. Versión actual: 0.7.3.
Inicio rápido: stdio local (recomendado para Claude Desktop)
Obtén una clave en foura.ai/dashboard#api-keys (un clic, mostrada solo una vez al crearla, formato pk_live_...). Añade esto a la configuración de tu cliente MCP:
{
"mcpServers": {
"foura": {
"command": "npx",
"args": ["-y", "@fouradata/mcp"],
"env": { "FOURA_API_KEY": "pk_live_..." }
}
}
}
Detalle importante sobre Claude Desktop: cierra completamente Claude Desktop (
Cmd+Qen macOS) antes de editar el archivo de configuración. Si la aplicación sigue en ejecución, sobrescribirá tus cambios con su configuración en memoria al salir.
El comando npx descarga @fouradata/mcp en el primer inicio y lo ejecuta como un subproceso de tu cliente MCP. No requiere instalación global.
| Cliente | Dónde se ubica la configuración |
|---|---|
| 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 (define primero FOURA_API_KEY en env) |
| Cursor | ~/.cursor/mcp.json |
| Windsurf | ~/.codeium/windsurf/mcp_config.json |
| VS Code (extensión MCP) | .vscode/mcp.json |
Reinicia el cliente. Las herramientas (foura_auto, foura_single, foura_proxy, foura_browser) y seis prompts aparecerán en tu lista de herramientas.
Inicio rápido: alojado (Streamable HTTP)
Para los clientes compatibles con el transporte Streamable HTTP (Cursor, Windsurf, VS Code, Claude Code con --transport http), dirígelos al endpoint alojado en lugar de ejecutar un subproceso local:
{
"mcpServers": {
"foura": {
"url": "https://mcp.foura.ai/mcp",
"headers": {
"Authorization": "Bearer pk_live_..."
}
}
}
}
Para Claude Desktop, usa la configuración stdio anterior o conecta el endpoint alojado a través de mcp-remote:
{
"mcpServers": {
"foura": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.foura.ai/mcp", "--header", "Authorization: Bearer pk_live_..."]
}
}
}
Referencia del endpoint alojado
| Propiedad | Valor |
|---|---|
| URL | https://mcp.foura.ai/mcp |
| Transporte | HTTP con streaming (POST /mcp, respuestas SSE) |
| Autenticación | Authorization: Bearer pk_live_... por request |
| MCP-Protocol-Version | Según @modelcontextprotocol/sdk (actualmente 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05, 2024-10-07) |
| Desafío 401 | WWW-Authenticate: Bearer realm="foura-mcp" |
El desafío 401 no incluye ningún parámetro RFC 9728 resource_metadata, a propósito. Anunciar uno hace que un cliente compatible con OAuth inicie un flujo que este servidor no implementa. Envía tu clave de pk_live_ como un Bearer token y el 401 desaparecerá.
El servidor alojado es stateless. Cada request incluye su propia clave, la cual el servidor reenvía a la API de FourA como X-API-Key. Una sola clave da acceso a las cuatro herramientas.
Para proteger contra DNS-rebinding (CVE-2025-66414), el servidor valida el header Host (debe ser mcp.foura.ai o localhost) y el header Origin cuando está presente (en lista permitida: mcp.foura.ai, claude.ai, app.cursor.sh, app.cursor.com). Los emisores server-to-server (curl, clientes MCP en modo bridge stdio) no envían Origin y pasan directamente.
Herramientas
Las cuatro herramientas están anotadas con readOnlyHint: true y openWorldHint: true según la especificación MCP 2025-06-18. Los clientes que aprueban automáticamente herramientas confiables de solo lectura las ejecutan sin un modal de confirmación por request.
foura_auto es la opción predeterminada inteligente: proporciónale una URL y devolverá el contenido, eligiendo el método de obtención por ti. Las otras tres son las primitivas de menor nivel que orquesta; utilízalas cuando quieras un control explícito.
foura_auto
Proporciónale una URL cuando quieras que FourA elija el método de request. Realiza intentos acotados a través de las rutas disponibles de HTTP, proxy y navegador. Pasa validate en objetivos protegidos para que la response deba contener contenido que identifique la página real. Si ningún intento cumple con la validación, la herramienta devuelve un error en lugar de presentar una página de desafío como un éxito.
La response incluye detalles de finalización en meta y, de forma predeterminada, una session reutilizable con proxy, cookies y userAgent. Para un seguimiento simple, llama a foura_single con session.proxy como proxy, serializa las cookies como un header Cookie y envía session.userAgent como el header User-Agent. Para renderizado de JavaScript, pasa los valores de sesión a los campos correspondientes de foura_browser.
foura_single
Una request HTTP, una response de vuelta. Refleja POST /api/single/ de forma directa uno a uno.
Úsala para páginas estáticas, APIs JSON y HTML renderizado en servidor.
Cómo elegir qué navegador presentas
Una request presenta la versión más reciente de Google Chrome por defecto. Cuando un objetivo acepte un navegador y rechace otro, define browser (Chrome, Edge, Safari, Firefox o Tor), os (Windows, macOS, Android o iOS) o version, o pasa un id exacto de profile:
{
"method": "GET",
"url": "https://example.com",
"browser": "Firefox",
"os": "Windows"
}
La versión más reciente prevalece cuando coinciden varios perfiles. Una combinación inexistente devuelve un error que lista las opciones disponibles, por lo que nunca se envía un request como un navegador que no hayas elegido. La selección requiere unblocker, que está habilitado por defecto. El catálogo se publica en GET /api/profiles y no requiere API key.
Los mismos cuatro campos se encuentran dentro del objeto request de foura_proxy.
foura_proxy
Enruta un HTTP request a través de proxies rotativos con reintento automático. Úsalo cuando foura_single esté bloqueado o el objetivo requiera un país de salida específico.
Configura exitCountries con una lista de permisos estricta de códigos de país de dos letras visibles para el objetivo, según lo proporcionado por el usuario o los requisitos del objetivo:
{
"maxTries": 5,
"exitCountries": ["CZ", "GB"],
"request": {
"method": "GET",
"url": "https://example.com/pricing",
"browser": "Chrome",
"os": "Windows"
}
}
Los valores se recortan, se convierten a mayúsculas y se deduplican. Se excluyen los proxies con salidas desconocidas y la request nunca recurre a un país no solicitado. La selección utiliza los metadatos de país visibles para el destino más recientes disponibles, normalmente actualizados en un plazo de diez minutos; no es una búsqueda de geolocalización en vivo durante la request. No infieras el país que sirve la respuesta a partir de la dirección del host del proxy.
Una respuesta exitosa con alcance definido devuelve exitCountry y el ID proxy reutilizable. Comprueba que exitCountry pertenezca a la lista de permitidos solicitada. Si el pool actual no tiene coincidencias, la herramienta devuelve code: "no_eligible_proxy" con el alcance normalizado en details.exitCountries. Conserva ese alcance y reinténtalo más tarde. Cámbialo o amplíalo solo cuando el usuario modifique explícitamente el requisito. El alcance por país está incluido a partir del plan Startup. En un plan que no lo incluye, una llamada que envíe exitCountries se rechaza con un 403 y X-FourA-Limit: plan_limit_feature.
Si la página seleccionada necesita JavaScript más adelante, pasa el ID proxy devuelto a foura_browser.proxy para que el navegador reutilice la misma salida.
Establece exitClass: "premium" para un destino al que el pool estándar no pueda acceder sin importar cuántas salidas se intenten. Es una autorización, no una instrucción: el pool estándar sigue compitiendo por la respuesta y normalmente gana, y una request que responda antes de que se intente cualquier salida premium no consume tráfico premium. Un intento premium contabiliza el tráfico que transportó incluso si falla. La respuesta devuelve exitClass, premium o standard, para que puedas ver por request qué clase te atendió. standard también es la respuesta una vez agotado el tráfico premium incluido en tu plan, y es un resultado normal en lugar de un error. exitClass: "standard" prohíbe el escalado por completo. exitClass: "premium" en un plan sin salidas premium se rechaza con code: "plan_limit_premium". Consulta exitClass.
Cuando la rotación tuvo que cambiar a otra familia de navegadores para obtener una respuesta, una respuesta exitosa incluye profile con la familia elegida. Vuelve a ejecutar con ella, o la siguiente llamada repetirá la versión que falló.
Una rotación fallida incluye attemptReport junto al error: una frase summary, más recuentos que separan las salidas que nunca respondieron (noResponse), las salidas que un control de bots rechazó (defense, con los proveedores en vendors), las páginas que llegaron y fueron rechazadas solo por tu propio validate.data (contentRejected), statusRejected y other. profilesTried enumera los navegadores que envió la tarea, en orden de primer uso, donde default significa que la request se envió exactamente como se escribió. Un valor alto de contentRejected significa que FourA entregó páginas reales y tu propia regla las descartó. Consulta Why a Proxy Request Ran Out of Tries.
foura_browser
Sesión de navegador completa. JavaScript se ejecuta, el DOM se renderiza, las cookies se devuelven. Refleja POST /api/browser/.
Utilízalo para aplicaciones de una sola página (SPA), contenido con carga diferida o páginas con una comprobación que requiera un navegador real para completarse.
Para conocer las estructuras de entrada, los valores predeterminados y las reglas de validación de cada herramienta, consulta la referencia de endpoints REST. Los esquemas de las herramientas coinciden campo por campo con la API REST, además de la opción exclusiva de MCP offload_large (ver más abajo).
Cuando un objetivo ejecuta una verificación de bots
foura_single y foura_proxy devuelven defense cuando el objetivo ejecutó una verificación de bots antes de entregar el cuerpo. defense.solved: true significa que la verificación se superó y data es la página real; false significa que el cuerpo puede ser una página de desafío. Reintenta con un navegador, sistema operativo o versión diferente, o pasa a foura_proxy o foura_browser, en lugar de tratar la página de desafío como contenido válido.
Respuestas tipadas
Cada respuesta de herramienta incluye tanto content (resumen de texto legible por humanos) como structuredContent (JSON tipado y validado contra el outputSchema de la herramienta). Cada herramienta tiene su propia estructura única:
foura_auto:{ status, headers, data }con estructura simple másmeta({ rung, solved, attempts, credits }, siempre presente, donderunges uno decache,probe,proxy,browser,warmup,fail) y, de forma predeterminada,session({ proxy, cookies, userAgent }) para reproducir mediante las herramientas de nivel inferior. Sintotal_time.foura_single:{ status, headers, data, total_time, ... }(headers es un array, una entrada por cada salto de redirección)foura_proxy: igual que la versión simple más{ proxy, total }; un éxito con ámbito también incluyeexitCountry, una request que especificó una clase incluyeexitClass, una rotación que cambió de familia de navegador incluyeprofile, y un fallo incluyeattemptReportfoura_browser: estructura distinta{ status, headers: object, body, cookies, userAgent }(nota:bodypuede ser una cadena o un objeto según el content-type)
Cada herramienta también informa cuánto costó la llamada y cómo rastrearla, datos leídos de los headers de respuesta de la API:
credits: créditos que consumió esta llamada. Presente también en los fallos, ya que el trabajo se realizó de todos modos. Solo se te factura por llamadas exitosas, por lo que un fallo muestra sus créditos aquí pero no tiene costo para ti.request_id: id de FourA para la llamada. Indícalo al abrir una solicitud de soporte.exitClass:premiumcuando una salida premium atendió la llamada. Enfoura_singleyfoura_browseresto ocurre cuandoproxyreproduce una salida quefoura_proxyencontró.
Cada uno de estos campos está ausente cuando la API no reportó nada, lo que permite que un cliente programado para una versión anterior siga funcionando sin cambios. Los mismos valores están documentados en Response Headers.
Los clientes compatibles con structuredContent pueden pasar el objeto tipado directamente al LLM en lugar de obligarlo a parsear JSON a partir de texto.
Headers de respuesta con múltiples valores
Los headers que aparecen varias veces (Set-Cookie, Link, WWW-Authenticate) se devuelven como arrays:
{
"headers": [
{
"result": { "version": "HTTP/2", "code": 200, "reason": "" },
"content-type": "text/html",
"set-cookie": ["a=1; Path=/", "b=2; Path=/"]
}
]
}
Esto es importante para sitios que configuran cookies de sesión + rastreo + consentimiento en una sola response (la mayoría del e-commerce).
Large responses: offload_large (default: inline)
Por defecto (desde v0.2.0), los cuerpos completos de las responses se devuelven inline en structuredContent sin importar el tamaño. Esto funciona en todos los clientes MCP de forma predeterminada.
Si tu cliente admite MCP resources/read Y quieres ahorrar tokens en páginas grandes, pasa offload_large: true por llamada a la tool. Las responses >= 50 KB se escriben entonces en el disco, se devuelven como resource_link, y tu cliente obtiene el body solo cuando realmente lo necesita. En el servidor alojado, los payloads en caché caducan después de 1 hora. En tu propia instancia nada elimina los payloads almacenados: limpia tú mismo los archivos con más de una hora de antigüedad del directorio de payloads.
{
"method": "GET",
"url": "https://en.wikipedia.org/wiki/Web_scraping",
"offload_large": true
}
| Cliente | offload_large: true |
|---|---|
| Claude Desktop | aún no, deja el valor predeterminado false |
| Claude Code, Cursor, Windsurf | compatible |
| Extensión MCP de VS Code | compatible |
Aislamiento por tenant: cada API key obtiene su propio namespace (sha256(apiKey)[:16]). Solo la key que almacenó un payload puede volver a leerlo. Las lecturas entre distintos tenants devuelven Payload not found sin filtrar información sobre su existencia.
Prompts integrados
Seis plantillas de flujo de trabajo aparecen bajo /prompts en cualquier cliente MCP. Cada una acepta argumentos con nombre y devuelve un mensaje de usuario basado en plantillas que orquesta una o más herramientas.
| Prompt | Argumentos | Qué hace |
|---|---|---|
smart_fetch |
url, opcional must_contain, extract |
Auto fetch (elige el método, gestiona la protección contra bots) y luego devuelve o extrae el contenido |
scrape_product_page |
url |
Fetch con navegador y luego extrae el título del producto, precio, imagen, stock y SKU como JSON |
extract_article |
url |
Single con fallback a proxy, luego elimina navegación y anuncios para devolver un JSON limpio del artículo |
monitor_pricing |
url, opcional target_price |
Fetch mediante proxy, extrae el precio actual y lo compara con el objetivo |
check_endpoint_health |
url, opcional expected_text |
Single con validación estricta, devuelve accesibilidad y tiempos |
bulk_fetch_urls |
urls (separadas por comas) |
Single en paralelo con fallback automático a proxy por cada URL, devuelve solo metadatos |
Los prompts consumen cero tokens mientras están inactivos. Solo los prompts invocados ingresan al contexto del LLM.
Texto completo y prompts de fallback manual: Recetas MCP.
Envoltorio de error
Cada error (isError: true) incluye un envoltorio structuredContent. Campos mínimos en cada error:
{
"service": "auto | single | proxy | browser",
"code": "rate_limited",
"error": "Rate limit exceeded"
}
En errores upstream con estado HTTP, status también está presente. En errores de rate limit y capacidad, el sobre upstream añade retryAfter, current.{concurrency, rpm} y limits.{maxConcurrency, maxRpm}. Consulta API Errors para ver la estructura REST subyacente.
Valores estables de code:
| Código | HTTP | Significado | ¿Reintento seguro? |
|---|---|---|---|
ssrf_blocked |
n/a | El destino es una dirección privada o reservada (RFC 5735, 6598, IPv6 reservada), la URL no es http(s) o su nombre de host no se resolvió | No, verifica la URL. Se puede reintentar una resolución que falló brevemente |
upstream_non_json |
varía | El upstream devolvió un cuerpo mal formado | Quizás, investigar |
output_validation_failed |
n/a | El outputSchema del servidor MCP rechazó la respuesta upstream, o la herramienta no pudo completar la llamada en absoluto (no hay API key configurada, API inaccesible) |
Quizás: verifica la configuración y luego reporta |
bad_request |
400 | Estructura de entrada rechazada | No, corrige los argumentos |
auth_failed |
401 | Clave faltante, inválida o desactivada | No, corrige la clave |
forbidden |
403 | El destino respondió 403 y tu validate lo rechazó (una verificación del sitio, una restricción de país) |
No, o cambia a foura_proxy |
not_found |
404 | Destino o endpoint no encontrado | No |
rate_limited |
429 | Límite de RPM alcanzado | Sí, espera retryAfter |
at_capacity |
503 | Límite de concurrencia alcanzado | Sí, espera retryAfter |
service_disabled |
503 | El servicio está deshabilitado por mantenimiento. Una herramienta que tu plan no incluye devuelve plan_limit_feature |
Contacta a soporte |
service_unavailable |
503 | 503 genérico | Sí, backoff corto |
upstream_error |
500+ o 0 | El destino respondió con un error de servidor, o en foura_proxy, foura_browser y foura_auto nunca respondieron |
Sí, backoff exponencial |
upstream_client_error |
4xx | Otro 4xx | Usualmente no |
upstream_unknown |
otro | La request se ejecutó pero no produjo una respuesta aceptada: en foura_single el destino nunca respondió (timeout, conexión rechazada), y en cualquier herramienta tu validate rechazó una respuesta 2xx o 3xx. Lee status y error |
Investigar |
no_eligible_proxy |
n/a | Ningún proxy coincide con el alcance estricto de exitCountries |
Reintenta más tarde; cambia el alcance solo de forma explícita |
plan_limit_* |
403 o 429 | Uno de los límites de tu plan rechazó la llamada: plan_limit_ seguido de feature, premium, concurrency, rate, browser_daily, credits o bandwidth. Consulta MCP Server Errors |
Espera retryAfter cuando esté presente; de lo contrario, no reintentes hasta que el límite se restablezca o el plan cambie |
Los agentes LLM pueden leer code directamente para la lógica de reintento sin parsear texto. Guía paso a paso de autenticación: Authentication.
Limits
- Inline body por defecto. Con
offload_large: true, las respuestas >= 50 KB van a disco +resource_link(por tenant, TTL de 1 hora). - Se rechazan los destinos privados (RFC 5735, RFC 6598, bloques reservados de IPv6) en la capa MCP. Solo se reenvían hosts públicos.
- Límite de request body de 256 KB en solicitudes
/mcpentrantes (los payloads reales de MCP son < 4 KB). - Los rate limits son aplicados por la FourA API por servicio. Consulta Rate Limits.
Self-Hosting
El código fuente completo del servidor es público en GitHub bajo @fouradata/mcp. Clona el repositorio, ejecuta npm install, npm run build y corre node dist/http.js para levantar tu propia instancia. Se ejecuta stateless en un único contenedor detrás de cualquier balanceador de carga.
Variables de entorno configurables:
| Variable | Default | Purpose |
|---|---|---|
PORT |
3076 |
Puerto de escucha HTTP |
FOURA_API_BASE |
https://api.foura.ai/api |
URL base de la FourA REST upstream |
FOURA_MCP_PAYLOADS_DIR |
una carpeta foura-mcp-payloads en el directorio temporal del sistema (el archivo Docker Compose incluido establece /data/payloads) |
Dónde se almacenan en caché en disco las respuestas >= 50 KB (con offload_large: true) |
FOURA_MCP_ALLOWED_HOSTS |
mcp.foura.ai,localhost,127.0.0.1,[::1] |
Lista de permitidos de hostnames para el header Host (defensa contra DNS-rebinding) |
FOURA_MCP_ALLOWED_ORIGINS |
https://mcp.foura.ai,https://claude.ai,https://app.cursor.sh,https://app.cursor.com |
Lista de permitidos de Origin para clientes de navegador |
El contenedor oficial se ejecuta como uid 1001 (no root). El bind mount del host /data/payloads debe permitir escritura por ese uid.
Escala horizontalmente detrás de cualquier balanceador de carga. Los clientes proporcionan su clave en cada request, por lo que no hay sticky sessions.