local-web-mcp
local-web-mcp
Versión 0.3.0 · AGPL-3.0
Esta herramienta se ha desarrollado con apoyo de IA, pero ha sido revisada por una persona antes de su publicación.
Un servidor MCP que le da a Claude un recuperador web de respaldo que se ejecuta en tu propia máquina. Cuando el recuperador integrado está bloqueado, este lo intenta desde tu IP, tu conexión y, opcionalmente, tu sesión iniciada.
Una única herramienta: fetch_url_locally.
El problema que resuelve
Los recuperadores alojados se ejecutan desde rangos de IP de centros de datos, que muchos sitios rechazan directamente. Un servidor MCP local por stdio no: el cliente lanza el proceso en tu máquina, por lo que las solicitudes se originan desde tu conexión habitual.
Funcionamiento verificado en sitios que el recuperador alojado no podía leer, incluidos CNBC, Politiken y Bloomberg.
Related MCP server: Proxy Browser MCP Server
Lo que no cambia
Conviene decirlo claramente, porque el planteamiento invita a exagerar.
La inferencia del modelo sigue ejecutándose en los servidores de Anthropic.
El texto de la página recuperada se sigue enviando al modelo como salida de la herramienta. Todo lo que se lea a través de una sesión autenticada se incluye con él.
Cambia quién habla con el sitio web, no dónde se ejecuta el modelo.
El límite infranqueable
¿Se puede leer la página sin ejecutar JavaScript?
Si es así, esto se encarga. Si no, nada en la capa de cabeceras, TLS o cookies conseguirá llegar, y ningún ajuste de User-Agent servirá de ayuda. Los sitios que usan DataDome, Cloudflare Turnstile, PerimeterX y similares presentan un desafío que debe ejecutarse para superarlo. Para esos, usa una herramienta basada en navegador o abre la página tú mismo.
Es un límite que hay que conocer, no un error que haya que corregir.
Veredictos
Cada respuesta viene etiquetada. El caso peligroso no es un 403 evidente, sino un 200 que no es la página: un intersticial antibots, un muro de consentimiento, una cáscara vacía renderizada en el cliente. Esos se resumen como si fueran el artículo.
Veredicto | Significado | Qué hacer |
| Contenido real | Úsalo |
| Protección antibots detectada | Requiere navegador, detente |
| Rechazado directamente (403), normalmente detección de bots | Requiere navegador, detente |
| Cáscara renderizada en el cliente, sin contenido sin JS | Requiere navegador, detente |
| 401, muro de inicio de sesión o muro de suscripción | Las cookies pueden ayudar |
| Cualquier otro estado no 2xx (404, 429, 5xx) | Reintenta más tarde solo para 429/5xx |
| Menos de 200 caracteres extraídos | Lectura fallida, no una página vacía |
Cualquier cosa que no sea ok añade una advertencia explícita que le dice al
modelo que no trate el cuerpo como contenido.
La evidencia del cuerpo tiene más peso que el código de estado. Un editor que
sirve un artículo de pago como HTTP 403 se informa como login_required, no
como blocked, porque el consejo útil ahí es exportar las cookies en lugar de
recurrir a un navegador.
La detección se basa en la infraestructura del proveedor en lugar del texto:
las páginas de desafío están localizadas, así que captcha-delivery.com es una
señal fiable donde «verificando tu dispositivo» no lo es. Las cookies de
proveedores en las cabeceras de respuesta cuentan como evidencia cuando se
combinan con un estado de rechazo.
La herramienta
fetch_url_locally(url, max_chars=20000, use_session=true)
Parámetro | Tipo | Por defecto | Propósito |
| string | obligatorio | La página que se va a recuperar. Solo |
| integer |
| Límite del texto devuelto, limitado a 200000. El truncamiento se indica en la cabecera |
| boolean |
| Envía cookies. Pasa |
La respuesta es una cabecera breve seguida del texto extraído:
URL: https://example.com/article
Status: 200
Verdict: ok
Title: The headline
Session: authenticated (the user's cookies were sent)
The article text...Session: aparece solo cuando las cookies se cargaron realmente y estaban
limitadas a ese host. Un veredicto distinto de ok añade una línea WARNING:
que indica al modelo que no trate el cuerpo como contenido.
Los fallos se devuelven como un error que indica la causa, por ejemplo
[blocked_host] Host '192.168.1.5' resolves to a private or internal address.
Códigos: invalid_url, blocked_scheme, blocked_host, dns_failure,
timeout, too_many_redirects, upstream_error, invalid_argument.
Requisitos
Python 3.11+ (usa
asyncio.timeout). Con uv no necesitas instalarlo tú mismo; uv descarga un intérprete adecuadoClaude Desktop o Claude Code. Los servidores Stdio no funcionan en el navegador ni en la aplicación móvil, que requieren un endpoint HTTPS accesible públicamente.
Instalación
Con uv, que descarga un Python adecuado y las dependencias por sí mismo:
uv --directory /absolute/path/to/local-web-mcp run local-web-mcpEjecuta eso una vez en una terminal antes de registrar el servidor. La primera ejecución resuelve y construye el entorno, lo que tarda lo suficiente como para que un cliente que lo lance en frío pueda rendirse y notificar que el servidor ha fallado aunque la configuración sea correcta.
Imprime Starting local-web y luego espera a un cliente en stdin, que es el
aspecto de un servidor en funcionamiento. En cuanto veas esa línea, el entorno
está construido: pulsa Ctrl+C y continúa.
O con un entorno virtual simple:
python3 -m venv .venv
.venv/bin/pip install -r requirements.txtRegistro con Claude Desktop
Añádelo a claude_desktop_config.json (macOS:
~/Library/Application Support/Claude/, Linux: ~/.config/Claude/):
{
"mcpServers": {
"local-web": {
"command": "uv",
"args": [
"--directory",
"/absolute/path/to/local-web-mcp",
"run",
"local-web-mcp"
]
}
}
}Si uv no está en el PATH que ve el cliente, usa su ruta absoluta como
command. El equivalente con el entorno virtual es:
{
"mcpServers": {
"local-web": {
"command": "/absolute/path/to/.venv/bin/python",
"args": ["/absolute/path/to/local_web_mcp.py"]
}
}
}Claude Code:
claude mcp add local-web -- uv --directory /absolute/path/to/local-web-mcp run local-web-mcpConfiguración
Todo es opcional. Cada ajuste tiene un valor predeterminado que funciona.
Variable | Por defecto | Propósito |
| sin definir | Ruta a un archivo de cookies |
| sin definir | Dominios para los que se puede usar el archivo. Establécelo siempre que definas un archivo de cookies |
|
| Permitir destinos privados, de bucle local y de enlace local |
| vacío | Nombres de host exentos de la comprobación de IP |
| Chrome UA | Se envía en cada solicitud |
|
| Preferencia de idioma. Los sitios la usan para elegir un idioma y a veces una edición regional |
|
| Segundos |
|
| Segundos |
|
| Tope máximo por llamada |
|
| Tope del cuerpo de la respuesta |
|
| Saltos de redirección, compartidos con los meta refrescos |
El Accept-Language predeterminado pide danés primero. Es un valor
predeterminado deliberado y no neutro, así que define LOCALWEB_ACCEPT_LANGUAGE
si quieres inglés o cualquier otra cosa:
"env": { "LOCALWEB_ACCEPT_LANGUAGE": "en-GB,en;q=0.9" }Cookies de sesión
Apunta LOCALWEB_COOKIE_FILE a una exportación de una extensión de navegador.
El cargador acepta cookies.txt de Netscape con cualquier cabecera o sin
ninguna, y exportaciones JSON de las extensiones habituales. Consulta
cookies.txt.example.
Define siempre LOCALWEB_COOKIE_DOMAINS. Sin ello, todo el archivo está
activo y cualquier recuperación puede llevar una sesión no relacionada. Con
ello, cualquier cookie fuera de esos dominios se descarta en el momento de la
carga, antes de que el archivo llegue a un cliente. La coincidencia cubre
subdominios (example.com cubre www.example.com), pero no imitaciones
(evil-example.com).
Tres cosas a tener en cuenta:
El contenido leído a través de tu sesión se envía al modelo como salida de la herramienta.
Un archivo de cookies es un archivo de credenciales. Haz
chmod 600, y aquí está en el .gitignore.Las cookies caducan. Una fuente que antes funcionaba y devuelve
login_requiredsuele ser una exportación caducada, no una pérdida de acceso.
Pasa use_session=false para comprobar si una página es realmente pública.
Acceso a una red local
Las direcciones privadas están bloqueadas por defecto, y de forma deliberada. El
modelo elige las URLs en parte a partir del texto que acaba de leer, por lo que
una página recuperada puede intentar dirigirlo hacia 192.168.1.1. La lista de
bloqueo hace que una inyección de instrucciones en el contenido de la página no
pueda convertir esto en un escáner de red.
Prefiere una lista de permitidos limitada antes que el interruptor general:
"env": { "LOCALWEB_ALLOWLIST": "nas.local,nas" }Seguridad
Esquema restringido a http y https; se rechazan las URLs que contienen credenciales
Nombre de host resuelto y cada dirección devuelta validada antes de la solicitud
El IPv4 mapeado en IPv6 (
::ffff:10.0.0.1) se desenvuelve antes de la comprobaciónLos endpoints de metadatos de la nube están bloqueados incondicionalmente: el nombre de host se resuelve antes que la lista de permitidos y que
ALLOW_PRIVATE, por lo que ninguno de los dos puede abrirlosLas redirecciones y los meta refrescos se siguen manualmente y se revalidan en cada salto
Se registran los recuentos de cookies, nunca nombres, valores ni dominios
Limitaciones conocidas
Sin JavaScript. El límite infranqueable mencionado antes.
DNS rebinding. El nombre de host se resuelve para la validación y luego se resuelve de nuevo al conectar. Cerrar esa brecha requiere fijar la conexión a la IP validada. Merece la pena hacerlo antes de activar
ALLOW_PRIVATEen una red no confiable.La detección de desafíos se basa en firmas y quedará obsoleta a medida que los proveedores cambien su marcado. Ejecuta
inspect_response.pysi un veredicto parece incorrecto.La extracción de HTML a texto no tiene dependencias y es rudimentaria.
trafilaturasería notablemente mejor para la extracción seria de artículos.
Archivos
Archivo | Propósito |
| El servidor |
| Cargador de cookies tolerante. Requerido, importado por el servidor |
| Diagnóstico: vuelca lo que el recuperador recibe realmente |
| Veredictos, validación de destinos, carga y ámbito de cookies |
| Destinos bloqueados y una recuperación en vivo, a través de stdio |
| Plantilla anotada para un archivo de cookies |
| Metadatos del paquete y el punto de entrada |
| Versiones de dependencias fijadas para instalaciones reproducibles |
| Dependencias de ejecución para la ruta del entorno virtual simple |
| Excluye el venv, las cachés y cualquier archivo de cookies |
| Texto completo de AGPL-3.0 |
Diagnóstico de un veredicto incorrecto
uv run python inspect_response.py https://example.com/articleImprime el estado, las cabeceras interesantes, el comienzo del cuerpo y, a continuación, ejecuta la detección del propio servidor sobre ello. Compruébalo siempre antes de cambiar la lógica de detección. Con frecuencia, un navegador y este recuperador reciben respuestas totalmente distintas, por lo que lo que ves en pantalla no es evidencia de lo que recibió la herramienta.
Pruebas
uv run python unit_test.py
uv run python smoke_test.pyunit_test.py funciona sin conexión y termina con un código de salida distinto de cero en caso de error, por lo que sirve como puerta de pre-commit. smoke_test.py controla el servidor mediante stdio y hace una solicitud real, por lo que necesita conexión de red. Ejecute ambos desde la raíz del repositorio.
En la ruta de venv simple, sustituya .venv/bin/python por uv run python.
Licencia
Copyright (C) 2026 David Lindholm.
GNU Affero General Public License v3.0 o posterior. Véase LICENSE.
Este programa se distribuye con la esperanza de que sea útil, pero SIN NINGUNA GARANTÍA; incluso sin la garantía implícita de COMERCIABILIDAD o IDONEIDAD PARA UN FIN PARTICULAR.
Available Tools
1 toolfetch_url_locallyFetch a URL from the user's machineARead-only
Fetch a web page over the user's own internet connection and return its readable text.
USE THIS WHEN the built-in web fetch has already been tried and: it returned an error, 403, or 429; it returned a Cloudflare or captcha interstitial; it returned a cookie/consent wall or a near-empty page; the source is geo-restricted; the source sits behind the user's institutional access or paid subscription; or the URL is on the user's local network.
DO NOT use it as the first attempt for ordinary public pages -- the built-in fetcher is faster and does not use the user's bandwidth.
The response carries a verdict line. If the verdict is not 'ok', the body is an interstitial rather than the article and must not be summarised as though it were the content.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | ||
| max_chars | No | ||
| use_session | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Clearly discloses that the response includes a verdict line that must be checked, and warns that a non-'ok' verdict means the body is an interstitial and must not be summarized as content. This is valuable behavioral context beyond the readOnlyHint annotation, and it directly prevents a likely misuse.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is appropriately sized for the complexity, with the core purpose front-loaded and usage conditions clearly structured. It earns its length by giving concrete error cases and a safety caveat; it is not overly verbose, though it could be slightly tightened.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity and the fact that there is an output schema, the description is complete for an agent to call it correctly: it knows when to use it, what to expect in the response, and how to avoid misinterpreting interstitials. No critical operational detail is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate; it does not mention individual parameters like max_chars or use_session, but the core parameter 'url' is fully implied ('Fetch a web page...'). The behavioral verdict warning adds context that indirectly helps the agent interpret the output, but explicit parameter-level guidance is still missing.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific action ('Fetch a web page over the user's own internet connection') and an explicit resource ('user's own... URL'), and it differentiates itself by emphasizing the local-network/user-machine execution path versus the built-in fetcher. It is unambiguous about what the tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides extensive when-to-use criteria: use only after built-in fetch fails with errors, interstitials, geo-restrictions, etc., and explicitly says DO NOT use as first attempt for ordinary pages. This is exactly the kind of conditional routing guidance an agent needs.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
1 tool update
v0.3.0- First observed
fetch_url_locally
TDQS
Scored across 1 tool
There is only one tool, so there is no possibility of confusing it with another tool. Its purpose is clearly described and scoped.
The single tool name follows a clear, descriptive snake_case convention. With only one tool, there are no inconsistent patterns to evaluate.
A single tool is borderline for a server, but it may be justified for a narrowly-focused fallback fetch utility. It feels thin compared to broader MCP servers, but not absurdly so.
The tool completely covers its stated purpose of fetching pages locally and returning readable text. Minor gaps exist around request customization or response metadata, but no core workflow dead ends are apparent.
Maintenance
Related MCP Connectors
Let your AI agent operate any website through a real, logged-in browser session.
Reliable web access for AI agents: smart HTTP, rotating proxies, and full-browser rendering.
Connect AI agents to your own Chrome with scoped access, approvals, and verified task results.
Give AI agents a real cloud Chrome: open sessions, navigate, read pages, click, type and screenshot.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceUnlimited, session-authenticated web search and fetch for AI tools using your own browser. Supports authenticated/paywalled pages without API keys.-
- FlicenseNot gradedqualityDmaintenanceEnables Claude to browse the web and make GET/POST/PUT requests with a real Chrome TLS/HTTP2 fingerprint to avoid anti-bot detection, supporting persistent cookie sessions and optional proxies.-
- FlicenseNot gradedqualityCmaintenanceProvides a direct bridge between Claude and an authenticated Chrome session, enabling fetch requests with session cookies and JavaScript execution within live web pages.-
- AlicenseNot gradedqualityCmaintenanceEnables Claude and other MCP clients to read, extract, crawl, and export data from open browser tabs and specified URLs locally, including logged-in sessions, structured page data, contacts, images, sitemaps, and Shopify catalogues.MIT