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+Q en 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ás meta ({ rung, solved, attempts, credits }, siempre presente, donde rung es uno de cache, probe, proxy, browser, warmup, fail) y, de forma predeterminada, session ({ proxy, cookies, userAgent }) para reproducir mediante las herramientas de nivel inferior. Sin total_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 incluye exitCountry, una request que especificó una clase incluye exitClass, una rotación que cambió de familia de navegador incluye profile, y un fallo incluye attemptReport
  • foura_browser: estructura distinta { status, headers: object, body, cookies, userAgent } (nota: body puede 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: premium cuando una salida premium atendió la llamada. En foura_single y foura_browser esto ocurre cuando proxy reproduce una salida que foura_proxy encontró.

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 /mcp entrantes (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.

Actualizado: 27 de septiembre de 2026