Communitygithub.com

lan-direct

Skill + navegador MCP para Claude Code y Codex: tu aplicación y navegador llegan a servicios locales a través del ISP real mientras el agente permanece en la VPN. Enlace por socket, ruteo por host, sin administrador, nada global.

¿Qué es lan-direct?

lan-direct es un skill para agentes de IA como Claude Code y Codex que permite que las aplicaciones y navegadores accedan a servicios domésticos a través del ISP real mientras el agente permanece conectado a la VPN. Funciona mediante enlace por socket y ruteo por host, sin modificar la tabla de enrutamiento, los proxies del sistema ni la configuración de la VPN. El skill incluye detección automática de la red, un proxy ligero para aplicaciones que no soportan enlace directo, y un servidor MCP para Playwright que permite navegación mixta (dominios locales por ISP, el resto por VPN). Soporta múltiples stacks tecnológicos (Node.js, Python, Go, PHP, etc.) y garantiza que no se toquen configuraciones globales ni se requieran permisos de administrador.

Compatible con✓Claude Code✓Codex CLI~Cursor
npx skills add farshadmomo/lan-direct

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

¿Qué hace lan-direct?

Enruta el tráfico de la aplicación a través del ISP mientras tú, el agente, permaneces en la VPN.

Funciona tanto en Claude Code como en Codex: el mecanismo es el enlace por socket, que no tiene nada que ver con el andamiaje.

El invariante

Nunca modifiques la tabla de enrutamiento, la configuración del proxy WinINET/del sistema ni la configuración del cliente VPN. Solo:

  • establece variables de entorno limitadas a un proceso hijo, o
  • pasa banderas de enlace de fuente por herramienta.

Ambos son por socket y optativos, por lo que no pueden afectar tu propia conexión. Este es el punto central del skill: si usas route add, lo has roto (consulta la sección Salida de emergencia al final).

No sugieras desconectar la VPN. Ese es el problema que este skill existe para evitar.

Flujo de trabajo

1. Detectar y verificar

powershell -NoProfile -ExecutionPolicy Bypass -File scripts\lan-detect.ps1

En Linux o macOS usa la contraparte en su lugar: el mismo JSON, el mismo contrato de código de salida:

bash scripts/lan-detect.sh

Emite JSON: lanIp, gateway, lanDns, subnet, tunnelActive, vpnAdapter, directEgress, lanEgress, fakeIpDns, bypassWorks. Sale con 0 solo cuando bypassWorks es verdadero: demuestra la omisión con dos sondas en vivo en lugar de inferirla.

  • tunnelActive: false -> no hay VPN activa. Dilo y detente; no hay nada que omitir.
  • bypassWorks: false con un túnel activo -> ve a Solución de problemas más abajo. No continúes. El tráfico permanecería silenciosamente en la VPN y reportarías un falso positivo.

2. Elegir el mecanismo

¿Backend Node (incl. Next.js)? Omite el proxy - salta a la receta Node en Recetas por stack. Una variable de entorno, sin proceso proxy, y es lo único que llega al fetch nativo.

Todo lo demás: inicia el proxy vinculado a la LAN. Un proceso servidor no tiene una bandera --interface, así que esta es la forma de obtener una salida al ISP.

node scripts/lan-proxy.mjs --bind <lanIp> --port 8899          # ejecutar en segundo plano
node scripts/lan-proxy.mjs --bind <lanIp> --port 8899 --dns <lanDns>   # si fakeIpDns: true

Escucha solo en 127.0.0.1 y registra cada host que tuneliza, para que puedas ver lo que la aplicación realmente llamó.

3. Reiniciar el backend apuntándolo al proxy

HTTP_PROXY=http://127.0.0.1:8899
HTTPS_PROXY=http://127.0.0.1:8899
NO_PROXY=localhost,127.0.0.1,<subnet>

NO_PROXY importa: sin él, las llamadas del backend a sus propios servicios locales se enviarían a través del proxy sin razón. (Las bases de datos locales en TCP - MySQL, Postgres, Mongo - no son HTTP y no se ven afectadas de todos modos).

Establece estas variables en el comando de lanzamiento del backend, no de forma global. Consulta Recetas por stack más abajo - HTTP_PROXY no es suficiente para varios stacks.

4. Ejercitar y confirmar

Golpea el endpoint cuya llamada saliente estaba fallando. Confirma ambos:

  • la llamada ahora tiene éxito, y
  • el registro del proxy muestra el host al que llamó.

Una prueba exitosa sin una línea de registro coincidente significa que la solicitud nunca pasó por el proxy.

5. Finalizar

Detén el proxy, reinicia el backend sin las variables de entorno. Nada más que deshacer: no se cambió ningún estado global.

pkill -f lan-proxy desde Git Bash no mata un node.exe de Windows. Usa:

Get-CimInstance Win32_Process -Filter "Name='node.exe'" |
  Where-Object { $_.CommandLine -like '*lan-proxy*' } |
  ForEach-Object { Stop-Process -Id $_.ProcessId -Force }

Una instancia sobrante es inofensiva, pero hará que el próximo lanzamiento falle con un conflicto de puertos.

Recetas por stack

StackCómo alimentarlo con el proxy
Python requests / httpxvariables de entorno, tal cual
Go, .NET HttpClientvariables de entorno, tal cual
PHP CLIlibcurl lee http_proxy / https_proxy del entorno del proceso
Node (incl. Next.js)no el proxy - usa la precarga lan-bind.mjs, ver más abajo
PHP bajo Apache/XAMPPver más abajo - el servicio no hereda tu shell
curl, wget, npm, ssh, gitomite el proxy, enlaza directamente (ver Enlace directo más abajo)

Node - usa la precarga, no el proxy

El fetch nativo se ejecuta sobre el undici integrado de Node, que ignora HTTP_PROXY en Node <= 23 y no acepta https.Agent. Verificado en Node 22.20: las variables de entorno simples, NODE_USE_ENV_PROXY=1 y --use-env-proxy no hacen nada: el registro del proxy permanece vacío y el tráfico sale silenciosamente a través de la VPN. Esto afecta a Next.js, el SDK de openai y cualquier otra cosa que use fetch nativo.

Omite el proxy por completo y usa la precarga, que enlaza cada socket saliente en el proceso:

LAN_BIND=<lanIp> node --import "file:///<directorio-del-skill>/scripts/lan-bind.mjs" script.js
LAN_BIND=<lanIp> NODE_OPTIONS="--import file:///<directorio-del-skill>/scripts/lan-bind.mjs" npm run dev

<directorio-del-skill> es el directorio base de este skill con barras diagonales - normalmente C:/Users/<tu>/claude/skills/lan-direct bajo Claude Code, o C:/Users/<tu>/.agents/skills/lan-direct bajo Codex. --import necesita una URL file:// absoluta en Windows; una ruta de Windows desnuda con barras invertidas falla.

NODE_OPTIONS se propaga a los workers bifurcados, por lo que cubre servidores de desarrollo Next.js y Vite.

Esto llega al fetch nativo, al SDK de openai, a axios, node-fetch, got y a http/https simple a la vez: sin dependencia, sin proceso proxy, sin necesidad de HTTP_PROXY. Omite los destinos de loopback, por lo que una base de datos local o un servidor de desarrollo siguen funcionando.

Solo si la precarga no está disponible: Node >= 24 honra NODE_USE_ENV_PROXY=1 con las variables de entorno, y axios las honra en cualquier versión.

PHP bajo Apache / XAMPP

SetEnv/PassEnv en httpd.conf solo puebla $_SERVER; libcurl lee el entorno del proceso, por lo que no los recogerá. Usa uno de:

  • iniciar Apache desde un shell que ya tenga http_proxy/https_proxy exportadas (p. ej. xampp\apache_start.bat), o
  • putenv('https_proxy=http://127.0.0.1:8899'); antes de curl_init(), o
  • curl_setopt($ch, CURLOPT_PROXY, '127.0.0.1:8899'); directamente.

file_get_contents() no honra ninguna de estas: necesita un contexto de flujo con 'proxy' => 'tcp://127.0.0.1:8899'.

Enlace directo

No se necesita proxy cuando la herramienta puede enlazar su propio socket:

HerramientaBandera
curl--interface <lanIp>
wget--bind-address=<lanIp>
npm--local-address=<lanIp>
ssh-b <lanIp>
Chromium / Playwrightno existe bandera de enlace - usa el MCP del navegador en el puerto 8900, ver más abajo

MCP de navegador / Playwright

Chromium no tiene una bandera de enlace de fuente, por lo que un navegador llega al ISP solo a través del proxy. Usa lan-browser.mjs: es un servidor MCP de Playwright que inicia y posee su propio proxy, por lo que el navegador nunca puede iniciarse sin uno.

Se incluye con este skill - scripts/install.ps1 lo registra:

powershell -NoProfile -ExecutionPolicy Bypass -File <directorio-del-skill>\scripts\install.ps1

Qué hosts usan el ISP proviene de tres fuentes combinadas, por lo que casi nunca es necesario reinstalar: lan-hosts.txt en el directorio del skill (valores predeterminados incluidos, reemplazados en git pull), ~/.lan-direct-hosts (los propios del usuario, nunca sobrescritos), y --lan-hosts a,b al iniciar. Una regla por línea, comentarios # permitidos, coincidencia de sufijo.

Cuando un usuario quiera que otro host sea enrutado a través del ISP, añade una línea a ~/.lan-direct-hosts y diles que reinicien el navegador - no edites lan-hosts.txt, que la próxima actualización sobrescribirá.

Si el usuario pide registrarlo manualmente, ten en cuenta que -- debe sobrevivir al enlazador de parámetros de PowerShell - pasa los argumentos como un array, o ejecútalo desde bash:

# desde un clon
claude mcp add playwright-split --scope user -- \
  node <directorio-del-skill>/scripts/lan-browser.mjs --lan-hosts ".ir"

# desde npm - sobrevive al movimiento del checkout, así que prefiero esto cuando el usuario lo tiene instalado
claude mcp add playwright-split --scope user -- npx -y lan-direct browser

Otros subcomandos de CLI, cuando el paquete está instalado: lan-direct detect elige el script de detección correcto para la plataforma, y lan-direct route <host> responde "¿este host iría a través del ISP o del túnel?" sin iniciar un navegador. Úsalo cuando un usuario reporte que un sitio no carga: una regla incorrecta y un sitio caído se ven idénticos desde fuera.

El servidor detecta la IP de la LAN, se niega a iniciarse si lan-detect dice que la omisión está rota, reutiliza un proxy existente si ya está escuchando, y apaga lo que inició cuando el MCP termina. Sus propios registros van a stderr; stdout se deja limpio para JSON-RPC.

Usa el puerto 8900, deliberadamente no el 8899 utilizado para las pruebas de backend anteriores. La reutilización es solo por puerto, por lo que compartir el número permitiría que un proxy de backend que enlace todo capture silenciosamente el navegador y enrute todo su tráfico a través del ISP.

La navegación mixta funciona en un mismo navegador. --lan-hosts hace que el proxy decida por host: los hosts coincidentes salen a través del ISP, todo lo demás queda sin enlazar y toma el túnel. Así que "revisa hoosha y youtube" no necesita cambiar de herramienta - hoosha sale a través del ISP, youtube a través de la VPN, misma sesión. El registro del proxy muestra la decisión en cada línea:

CONNECT  lan  hoosha.com:443
CONNECT  vpn  youtube.com:443

Mantén la lista precisa: un host doméstico que falta en ella obtiene el túnel y falla con una respuesta vacía, y un host extranjero añadido erróneamente obtiene el ISP y puede ser filtrado. Cuando un sitio falle de una manera que parezca una conexión muerta, comprueba qué ruta eligió el registro antes de asumir que el sitio está caído.

Por qué una lista explícita en lugar de detección automática: conectarse a un host bloqueado a través del túnel tiene éxito a nivel TCP (medido: connect@14ms) y solo muere ~860ms después, después de que el proxy ya haya respondido 200 Connection Established. La reversión implicaría almacenar en búfer y reproducir el handshake TLS y pagar ese retraso en cada solicitud doméstica.

Playwright enruta el loopback a través del proxy. Chrome normalmente omite localhost, pero Playwright lo anula cuando se establece un proxy, por lo que http://127.0.0.1:3000 llega a lan-proxy.mjs en lugar de ir directo. Verificado en el registro del proxy. La protección de loopback del proxy es lo que mantiene accesibles los servidores de desarrollo locales - sin ella, cada una de esas solicitudes fallaría con EADDRNOTAVAIL. No añadas --proxy-bypass=<-loopback>; no cambia nada aquí y solo confunde el panorama.

Solución de problemas

directEgress es igual a lanEgress con un túnel activo. La VPN está aplicando un kill-switch o una ruta estricta (un filtro WFP que bloquea los sockets enlazados). Arréglało en el cliente VPN, no a su alrededor: desactiva la ruta estricta, o añade una regla de enrutamiento directo/de omisión para los hosts que necesitas. nekoray/sing-box: desactiva strict_route, o añade una regla de salida direct.

En Linux esto también puede ocurrir sin ningún kill-switch: una dirección de fuente por sí sola no anula la tabla de enrutamiento allí como lo hace en Windows, por lo que el enrutamiento de políticas mantiene el tráfico en el túnel. La solución es una regla que envíe el tráfico desde la IP de la LAN a través de la puerta de enlace física - ip rule add from <lanIp> table <n> más una ruta predeterminada en esa tabla. Eso necesita root y es un cambio en todo el sistema, así que explícalo y deja que el usuario decida. Todo lo demás en este skill se mantiene sin privilegios y limitado al proceso.

fakeIpDns: true. El resolver del SO está devolviendo marcadores de posición 198.18.x.x que solo significan algo dentro del túnel. Pasa --dns <lanDns> al proxy para que resuelva a través del DNS propio de la LAN.

La sonda de salida no devuelve nada. No hay conectividad con el host de la sonda. Verifica el enlace, vuelve a ejecutar.

Salida de emergencia

Si un backend genuinamente no puede ser proxyficado (un binario compilado sin soporte de proxy), la única opción restante es una ruta estática específica para el host:

route add <targetIp> mask 255.255.255.255 <gateway> metric 1   # necesita admin
route delete <targetIp>                                        # deshacer, siempre

Esto es global. Saca todos los procesos del túnel para ese destino - incluyéndote a ti, si alguna vez contactas el mismo host. Requiere elevación, sobrevive hasta que se elimina y es fácil de olvidar. Pregunta antes de usarlo, y elimínalo en la misma sesión.

Skills relacionados