MCP + Nstdata Crawl: Construir un servidor de búsqueda web MCP para Claude
Resumen
Un servidor MCP de búsqueda en la web necesita dos herramientas, no una. Una herramienta de búsqueda/descubrimiento que devuelva URLs candidatas y una herramienta de obtención que devuelva contenido limpio de página para que el LLM lo lea; la mayoría de los ejemplos de una sola herramienta omiten la segunda parte.
Nstdata Crawl se ajusta a la parte de obtención de ese patrón. Renderiza páginas con mucho JavaScript y redirige solicitudes a través de la red de proxy de Nstdata, lo que aborda los problemas de detección de bots e inconsistencias geográficas que un simple solicitud HTTP enfrenta en una página de resultados de búsqueda.
Construir el servidor requiere dos paquetes de Python y alrededor de 50 líneas de código. La versión de este artículo fue instalada, importada y ejecutada contra el SDK real del Protocolo de Contexto del Modelo (MCP) en el proceso de redacción, incluida una conexión en vivo de stdio entre un cliente de prueba y el servidor.
Nstdata Crawl devuelve Markdown como una referencia de almacenamiento, no texto en línea. Una integración funcional tiene que resolver esa referencia con una segunda llamada antes de que un LLM pueda leer el contenido; un detalle que la mayoría de los ejemplos de inicio rápido omiten.
El mismo servidor se registra con Claude Desktop y Claude Code a través del mismo transporte de stdio, usando claude_desktop_config.json para el primero y claude mcp add para el segundo.
Cada llamada de búsqueda se factura como una obtención de Nstdata Crawl de una página de resultados, separada de cada llamada de lectura de página. Una sola pregunta de usuario que activa una búsqueda más tres lecturas de página cuesta cuatro obtenciones, no una.
Este patrón no reemplaza la comprensión de consultas o el ranking de una API de búsqueda diseñada para ese propósito. Es una forma de agregar acceso web en vivo a un agente utilizando una infraestructura de obtención que controlas de principio a fin, incluyendo cuál grupo de proxies y configuraciones de renderizado utiliza cada solicitud.
`NstDataClient.submit_scrape_task_sync` bloquea hasta que la obtención se complete, razón por la cual ambas funciones de herramienta son simplemente `def` en lugar de `async def` -- FastMCP soporta cualquiera. `Format.LINKS` y `Format.MARKDOWN` indican a Nstdata Crawl qué representaciones de la página devolver; solicitar solo lo que cada herramienta necesita mantiene la respuesta más pequeña y el paso de resolución de referencia de almacenamiento más barato.
El Protocolo de Contexto del Modelo (MCP) es un estándar de código abierto, introducido por Anthropic y ahora respaldado en una amplia gama de clientes y servidores, para conectar aplicaciones de IA a sistemas externos -- fuentes de datos, herramientas y flujos de trabajo -- a través de una interfaz única y consistente. Un LLM por sí solo no puede buscar en la web ni abrir una URL; solo puede trabajar con lo que está en sus datos de entrenamiento y cualquier texto que pegues en la conversación. Un servidor MCP cierra esa brecha al exponer herramientas invocables -- funciones con un nombre, una descripción y un esquema tipado -- que una aplicación host como Claude Desktop o Claude Code puede listar, llamar y alimentar los resultados nuevamente en la conversación.
Existen varios servidores MCP de búsqueda web gestionados ya si solo quieres que la búsqueda funcione en cinco minutos con la infraestructura de otra persona detrás. Construir el tuyo propio intercambia esa conveniencia por control: decides qué páginas obtienen renderizado de JavaScript, qué grupo de proxies y geolocalización utiliza cada solicitud, cómo se almacenan los resultados y qué sucede cuando un sitio objetivo bloquea la solicitud. Ese control es especialmente importante contra sitios que resisten activamente la raspadura HTTP simple, que es la mayoría de la web moderna, incluidas las propias páginas de resultados de motores de búsqueda; consulta la comparativa más amplia de Nstdata sobre APIs de raspado web para ver cómo se compara el renderizado y la capa de proxy de Crawl con otros backends de obtención fuera del contexto del MCP específicamente.
Nstdata Crawl es la capa de obtención que utiliza esta construcción para llenar esa brecha. Es una API de rastreo web orientada a la IA: dale una URL y devuelve Markdown limpio, HTML, enlaces, capturas de pantalla o salida PDF, con renderizado de JavaScript y el manejo de entrega por la red de proxies de Nstdata. Está diseñada específicamente para escenarios de uso de herramientas de agentes -- ingesta RAG, monitoreo y el patrón de búsqueda-luego-lectura que implementa este servidor -- en lugar de ser un marco de raspado que puedes ensamblar y mantener tú mismo. Tres cosas sobre esto son importantes para un servidor MCP en particular:
Renderizado de JavaScript -- las páginas de resultados de búsqueda y muchos sitios modernos renderizan su contenido del lado del cliente, por lo que una simple solicitud GET HTTP devuelve una estructura vacía; Crawl ejecuta la página en un navegador real primero.
Solicitudes respaldadas por proxy -- cada obtención se redirige a través de la red de proxies de Nstdata en lugar de la propia IP de la máquina que llama, lo que evita que un servidor que realice solicitudes repetidas a motores de búsqueda sea bloqueado tras unas pocas llamadas.
Grandes salidas referenciadas para almacenamiento -- Markdown, HTML y otros campos grandes regresan como un token de referencia que resuelves por separado, lo que mantiene la respuesta inicial pequeña incluso cuando el contenido de una página es largo.
Si las solicitudes web de su agente siguen siendo bloqueadas o se les sirve una shell de JavaScript en lugar de contenido real, Nstdata Crawl renderiza la página en un navegador real y la enruta a través de la red proxy de Nstdata antes de entregar a su servidor MCP un Markdown limpio.
Requisitos previos
Necesita Python 3.10 o más reciente; el paquete mcp oficial lo requiere; un host compatible con MCP como Claude Desktop o Claude Code, y una cuenta de Nstdata con un token de API Crawl, disponible en la página de productos de Nstdata Crawl después de registrarse. La documentación completa de puntos finales y parámetros para la API subyacente se encuentra en la documentación de Crawl de Nstdata, útil si desea agregar opciones como capturas de pantalla o salida PDF más allá de lo que utiliza este ejemplo. No necesita una clave de API de motor de búsqueda separada para esta construcción: la herramienta web_search que se presenta a continuación obtiene la propia página de resultados de un motor de búsqueda a través de Nstdata Crawl en lugar de llamar a una API de búsqueda dedicada.
Instalación
Cree un entorno virtual e instale los dos paquetes que necesita este servidor: el SDK de Python MCP oficial con sus complementos CLI y el SDK Crawl de Nstdata.
Ejecutar esto en el transcurso de la redacción de este artículo instaló mcp 1.27.0 y nstdata-ai-crawl 0.1.1, las versiones actuales de cada paquete en el momento de la redacción; pip install siempre se resuelve a lo que sea más nuevo cuando lo ejecuta. No se requieren otras dependencias: el SDK Crawl incluye su propio cliente HTTP internamente.
Configurar
Guarde lo siguiente como mcp_server.py. Define dos herramientas: web_search, que obtiene la página de resultados de un motor de búsqueda y devuelve los enlaces en ella, y read_page, que obtiene cualquier URL y devuelve su contenido en Markdown limpio.
import os
from mcp.server.fastmcp import FastMCP
from nstdata_ai_crawl import Format, NstDataClient, ScrapeRequestDto
NSTDATA_API_TOKEN = os.environ["NSTDATA_API_TOKEN"]client = NstDataClient(NSTDATA_API_TOKEN)mcp = FastMCP("nstdata-web-search")def_extract_markdown(result)->str|None:"""Extraer texto Markdown de un resultado de raspado.
Nstdata Crawl devuelve Markdown como una referencia de almacenamiento (markdownRef)
en lugar de texto en línea, por lo que cada resultado debe resolverse con
read_storage() antes de que un LLM pueda leerlo.
"""ifnot result.data ornot result.data.markdownRef:returnNonereturn client.read_storage(result.data.markdownRef).decode("utf-8")@mcp.tool()defweb_search(query:str, max_results:int=5)->str:"""Buscar en la web una consulta y devolver las URLs de los resultados candidatos.
Args:
query: La consulta de búsqueda.
max_results: Número máximo de enlaces de resultados a devolver (predeterminado 5).
""" result = client.submit_scrape_task_sync(ScrapeRequestDto( url=f"https://html.duckduckgo.com/html/?q={query}", formats=[Format.LINKS], onlyMainContent=False, timeout=60000,))ifnot result.success ornot result.data:returnf"La búsqueda falló: {result.errorMessage or result.errorCode}" links =[ link for link in(result.data.links or[])if"duckduckgo.com"notin link
][:max_results]return"\n".join(f"- {link}"for link in links)if links else"No se encontraron resultados."@mcp.tool()defread_page(url:str)->str:"""Obtener una URL y devolver su contenido en Markdown limpio.
Args:
url: La página a obtener y leer.
""" result = client.submit_scrape_task_sync(ScrapeRequestDto( url=url, formats=[Format.MARKDOWN], onlyMainContent=True, timeout=60000,))ifnot result.success ornot result.data:returnf"No se pudo leer la página: {result.errorMessage or result.errorCode}" markdown = _extract_markdown(result)return markdown or"Página obtenida pero no se devolvió contenido legible."if __name__ =="__main__": mcp.run(transport="stdio")
Antes de conectar esto a un host en vivo, verifica que el servidor realmente hable MCP correctamente, siguiendo el mismo patrón que la guía oficial de creación de servidores MCP. El cliente del SDK oficial puede comunicarse con un servidor a través del mismo transporte stdio que utiliza un host, por lo que puedes probar el apretón de manos sin que Claude Desktop o Claude Code estén en la mezcla:
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
asyncdefmain(): server_params = StdioServerParameters( command="python3", args=["mcp_server.py"], env={"NSTDATA_API_TOKEN":"dummy-key-for-handshake-test"},)asyncwith stdio_client(server_params)as(read, write):asyncwith ClientSession(read, write)as session:await session.initialize() tools =await session.list_tools()for t in tools.tools:print(f"- {t.name}: required={t.inputSchema.get('required')}")asyncio.run(main())
Ejecutar esto contra el servidor anterior produce:
=== Viaje redondo en vivo por stdio: tools.list ===
- web_search: required=['query']
- read_page: required=['url']
Esa salida es un viaje redondo real cliente-servidor: el cliente generó el servidor como un subprocesso, completó el apretón de manos MCP initialize y recibió ambas esquemas generados automáticamente de las herramientas a través de tools/list -- no se necesitó ninguna llamada a la API de Nstdata para este paso, ya que listar herramientas no toca la red.
Adjuntar capacidades
Claude Desktop lee las definiciones del servidor desde claude_desktop_config.json (~/Library/Application Support/Claude/claude_desktop_config.json en macOS, %AppData%\Claude\claude_desktop_config.json en Windows). Agrega el servidor bajo mcpServers:
Un estado de ✔ Conectado significa que Claude Code generó con éxito el proceso del servidor y completó el apretón de manos MCP con él -- no confirma que el token de Nstdata Crawl sea válido, ya que esa llamada solo ocurre cuando se invoca realmente una herramienta. Si una llamada a la herramienta falla más adelante con un error de autenticación, el token en sí es lo primero que hay que verificar, no el cableado de MCP.
Ejemplo funcional
Una vez que el servidor está adjunto, una solicitud como "qué cambió recientemente en la especificación de MCP, y resume el resultado principal" transcurre de la siguiente manera: Claude llama a web_search con una consulta sobre los cambios en la especificación de MCP, recibe un puñado de URLs candidatas, elige una que parece autorizada y llama a read_page en ella para extraer el contenido de la página real en la conversación antes de responder. La forma ilustrativa del valor de retorno de esa segunda llamada se ve así:
{"content":[{"type":"text","text":"# Registro de cambios del Protocolo de Contexto de Modelos\n\n## 2026-07-28\n\n- ..."}]}
Claude luego lee ese texto en Markdown de la misma manera que leería cualquier otra cosa en la conversación, y puede citar la URL de origen de la que provino, ya que esa URL fue el argumento de read_page en primer lugar.
Límites
web_search aquí obtiene la página de resultados de un motor de búsqueda y devuelve enlaces en bruto; no clasifica los resultados, no entiende la intención de la consulta, ni paginada de la manera en que lo haría una API de búsqueda diseñada para tal fin. El filtro "duckduckgo.com" not in link es una heurística ingenua para excluir los enlaces de navegación del propio motor de búsqueda, no un parser de resultados robusto; una versión de producción debería filtrar con más precisión o cambiar a un proveedor de búsqueda diseñado para uso programático si la calidad de clasificación importa. La facturación sigue el modelo de cobro por búsqueda de Nstdata Crawl: cada llamada a web_search y cada llamada a read_page es una búsqueda facturable separada, por lo que una sola pregunta de usuario que desencadena una búsqueda y tres lecturas de página cuesta cuatro búsquedas, y el código anterior no almacena en caché las consultas repetidas. Nstdata Crawl también limita cada solicitud a un tiempo de espera de 60 segundos y devuelve un HTTP 429 con un encabezado Retry-After cuando un llamador excede su límite de tasa; este ejemplo mínimo no implementa reintentos ni retrocesos, así que una versión de producción debería agregar ambos. Finalmente, considera esto como una herramienta para un agente que realice consultas limitadas y bajo demanda en lugar de scraping masivo; respeta los términos de servicio de los sitios de destino y robots.txt, y mantén el volumen de solicitudes proporcional a una pregunta de usuario real en lugar de realizar crawls desatendidos con las mismas credenciales.
Conclusión
Un servidor MCP de búsqueda web operativo se reduce a dos herramientas: una que descubre URLs candidatas y una que obtiene contenido limpio de una URL específica, conectadas a una capa de obtención que realmente pueda superar lo que los sitios web modernos hacen para bloquear el scraping HTTP simple. La representación de JavaScript y el acceso respaldado por proxy de Nstdata Crawl cubren esa capa de obtención, el SDK oficial de Python MCP maneja la plomería del protocolo, y el mismo servidor se conecta tanto a Claude Desktop como a Claude Code a través del mismo transporte stdio. Verifica el apretón de manos con un cliente real antes de conectarlo a un host, observa el modelo de facturación por búsqueda si tu agente busca y lee en cada pregunta, y trata el filtrado ingenuo de enlaces en este ejemplo como un punto de partida en lugar de un backend de búsqueda terminado.
Un servidor MCP es un programa que expone herramientas, datos o plantillas de aviso llamables a una aplicación de IA a través del Protocolo de Contexto del Modelo, para que la IA pueda llamar a funciones reales en lugar de solo generar texto.
P: ¿Necesito usar Nstdata Crawl específicamente, o puedo obtener páginas yo mismo con solicitudes HTTP simples?
Puedes obtener páginas con cualquier cliente HTTP, pero las solicitudes simples suelen fallar contra páginas renderizadas por JavaScript y son bloqueadas por sitios con detección de bots; Nstdata Crawl maneja ambos casos renderizando páginas en un navegador real y dirigiendo las solicitudes a través de su red de proxy.
P: ¿Esto le permite a Claude buscar en toda la web como un motor de búsqueda?
No. La herramienta web_search en este ejemplo obtiene la página de resultados de un motor de búsqueda y devuelve los enlaces en ella; no indexa la web en sí misma ni clasifica los resultados de la manera en que lo hace un motor de búsqueda dedicado.
P: ¿Cuánto cuesta ejecutar esto?
El costo depende de la actual tarifación por búsqueda de Nstdata Crawl, publicada en la página del producto Crawl, multiplicada por cuántas búsquedas y lecturas de páginas realiza tu agente realmente; cada uno es una búsqueda facturable separada.
P: ¿Puedo usar este servidor con Claude Code así como con Claude Desktop?
Sí. Ambos se conectan al mismo servidor stdio; Claude Desktop lee la definición del servidor desde claude_desktop_config.json, mientras que Claude Code la registra con claude mcp add.
P: ¿Está mi token de API de Nstdata expuesto al LLM?
No. El token vive en las variables de entorno del proceso del servidor y se usa solo dentro de mcp_server.py para autenticarse con Nstdata Crawl; el LLM solo ve los valores de retorno de la herramienta, nunca el token en sí.
P: ¿Qué sucede si el motor de búsqueda objetivo bloquea o limita la tasa de la solicitud?
Nstdata Crawl devuelve un resultado no exitoso con un código de error que la función web_search muestra como un mensaje de error en texto simple; este ejemplo mínimo no reintenta automáticamente, por lo que una versión de producción debería agregar retroceso y una estrategia de consulta de respaldo.
P: ¿Necesito desplegar este servidor en algún lugar, o puede ejecutarse localmente?
Puede ejecutarse completamente en tu propia máquina como un subprocesso local que Claude Desktop o Claude Code lanza a través de stdio; solo necesitas un despliegue público si estás exponiendo el servidor a un host remoto a través de HTTP.
Cómo raspar perfiles de LinkedIn en 2026: guía paso a paso
Raspar datos de perfiles públicos de LinkedIn con Python de manera compliant: qué es legal, qué decidió realmente hiQ v. LinkedIn, y solicitudes/código de BeautifulSoup funcional para nombre, titular, ubicación y rol actual — sin inicio de sesión, sin datos de conexiones, sin eludir la detección de bots de LinkedIn.
Kai Watanabe
Sep. 7th 2026
110M+ IP reales con 99.9% de acceso exitoso
Respuesta media ultrarrapida ~0.5s para tareas de alta concurrencia
Desde solo $0.1/GB
Acceso inmediato a pools premium de proxies residenciales, datacenter, IPv6 e ISP.