Communitygithub.com

lan-direct

Skill + MCP de navegador para Claude Code e Codex: seu aplicativo e navegador acessam serviços domésticos pelo provedor real enquanto o agente permanece na VPN. Vinculação por soquete, roteamento por host, sem admin, sem alterações globais.

O que é lan-direct?

O lan-direct é um skill para agentes de IA como Claude Code e Codex que resolve o problema de aplicativos e navegadores precisarem acessar serviços locais (domésticos) diretamente pelo provedor de internet (ISP) enquanto o agente permanece conectado à VPN. Ele funciona através de vinculação por soquete (per-socket binding), definindo variáveis de ambiente ou flags de ferramentas de forma escopada, sem modificar tabelas de roteamento, configurações de proxy do sistema ou configurações do cliente VPN — nenhuma alteração de estado global é necessária e nenhum privilégio de administrador é exigido. Suporta múltiplas stacks (Node, Python, Go, PHP, etc.) com receitas específicas para cada uma, e inclui um servidor MCP Playwright para navegadores que decide por host quais destinos rotear via ISP ou via túnel, permitindo navegação mista em uma única sessão sem alternância de ferramentas. O skill é composto por scripts de detecção que verificam se o bypass funciona com sondagens ativas, um proxy LAN leve, e um pré-carregamento para Node que vincula todos os soquetes de saída sem depender de variáveis HTTP_PROXY.

Funciona com✓Claude Code✓Codex CLI~Cursor
npx skills add farshadmomo/lan-direct

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

O que lan-direct faz?

Direcione o tráfego do aplicativo para fora pela ISP enquanto você, o agente, permanece na VPN.

Funciona tanto no Claude Code quanto no Codex — o mecanismo é vinculação por soquete, que não tem nada a ver com o invólucro.

O invariante

Nunca modifique a tabela de roteamento, as configurações de proxy do WinINET/sistema ou a configuração do cliente VPN. Apenas:

  • defina variáveis de ambiente com escopo de um processo filho, ou
  • passe flags de vinculação de origem por ferramenta.

Ambos são por soquete e opt-in, portanto não podem afetar sua própria conexão. Este é o ponto central do skill — se você recorrer a route add, você o quebrou (veja a seção Válvula de escape no final).

Não sugira desconectar a VPN. Esse é o problema que este skill existe para evitar.

Fluxo de trabalho

1. Detectar e verificar

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

No Linux ou macOS use a contraparte — mesmo JSON, mesmo contrato de código de saída:

bash scripts/lan-detect.sh

Emite JSON: lanIp, gateway, lanDns, subnet, tunnelActive, vpnAdapter, directEgress, lanEgress, fakeIpDns, bypassWorks. Sai com código 0 apenas quando bypassWorks é verdadeiro — ele prova o bypass com duas sondagens ativas, em vez de inferi-lo.

  • tunnelActive: false -> nenhuma VPN está ativa. Informe e pare; não há nada a ser desviado.
  • bypassWorks: false com um túnel ativo -> vá para Solução de problemas abaixo. Não prossiga. O tráfego permaneceria silenciosamente na VPN e você relataria um falso positivo.

2. Escolha o mecanismo

Backend Node (incl. Next.js)? Pule o proxy — vá direto para a receita Node em Receitas por stack. Uma variável de ambiente, sem processo de proxy, e é a única coisa que atinge o fetch nativo.

Todo o resto: inicie o proxy vinculado à LAN. Um processo servidor não tem uma flag --interface, então esta é a maneira de obter um egresso pela ISP.

node scripts/lan-proxy.mjs --bind <lanIp> --port 8899          # executar em segundo plano
node scripts/lan-proxy.mjs --bind <lanIp> --port 8899 --dns <lanDns>   # se fakeIpDns: true

Ele escuta apenas em 127.0.0.1 e registra cada host que tunela, para que você possa ver o que o aplicativo realmente chamou.

3. Reinicie o backend apontando para ele

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 é importante: sem ele, as chamadas do backend para seus próprios serviços locais são enviadas através do proxy sem motivo. (Bancos de dados locais em TCP — MySQL, Postgres, Mongo — não são HTTP e não são afetados de qualquer forma.)

Defina essas variáveis no comando de inicialização do backend, não globalmente. Veja Receitas por stack abaixo — HTTP_PROXY não é suficiente para várias stacks.

4. Execute e confirme

Acesse o endpoint cuja chamada de saída estava falhando. Confirme ambos:

  • a chamada agora é bem-sucedida, e
  • o log do proxy mostra o host que ele discou.

Um sucesso sem uma linha de log correspondente significa que a requisição nunca passou pelo proxy.

5. Encerramento

Pare o proxy, reinicie o backend sem as variáveis de ambiente. Nada mais a desfazer — nenhum estado global foi alterado.

pkill -f lan-proxy do Git Bash não mata um node.exe no Windows. Use:

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

Uma instância remanescente é inofensiva, mas fará a próxima inicialização falhar com conflito de porta.

Receitas por stack

StackComo alimentar o proxy
Python requests / httpxvariáveis de ambiente, como está
Go, .NET HttpClientvariáveis de ambiente, como está
PHP CLIlibcurl lê http_proxy / https_proxy do ambiente do processo
Node (incl. Next.js)não o proxy — use o pré-carregamento lan-bind.mjs, veja abaixo
PHP sob Apache/XAMPPveja abaixo — o serviço não herda seu shell
curl, wget, npm, ssh, gitpule o proxy, vincule diretamente (veja Vinculação direta abaixo)

Node — use o pré-carregamento, não o proxy

O fetch nativo roda no undici embutido do Node, que ignora HTTP_PROXY no Node <= 23 e não aceita https.Agent. Verificado no Node 22.20: variáveis de ambiente simples, NODE_USE_ENV_PROXY=1 e --use-env-proxy não fazem nada — o log do proxy permanece vazio e o tráfego sai silenciosamente pela VPN. Isso afeta Next.js, o SDK openai e qualquer outra coisa no fetch nativo.

Pule o proxy completamente e use o pré-carregamento, que vincula cada soquete de saída no processo:

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

<skill-dir> é o diretório base deste skill com barras normais — normalmente C:/Users/<voce>/.claude/skills/lan-direct no Claude Code, ou C:/Users/<voce>/.agents/skills/lan-direct no Codex. --import precisa de uma URL file:// absoluta no Windows; um caminho Windows puro com barras invertidas falha.

NODE_OPTIONS se propaga para workers bifurcados, cobrindo servidores de desenvolvimento Next.js e Vite.

Isso atinge fetch nativo, o SDK openai, axios, node-fetch, got e http/https simples de uma só vez — sem dependência, sem processo de proxy, sem necessidade de HTTP_PROXY. Ele ignora destinos loopback, então um banco de dados local ou servidor de desenvolvimento continua funcionando.

Apenas se o pré-carregamento não estiver disponível: Node >= 24 respeita NODE_USE_ENV_PROXY=1 com as variáveis de ambiente, e axios as respeita em qualquer versão.

PHP sob Apache / XAMPP

SetEnv/PassEnv no httpd.conf apenas preenche $_SERVER; o libcurl lê o ambiente do processo, então não os captará. Use um destes:

  • inicie o Apache a partir de um shell que já tenha http_proxy/https_proxy exportado (ex.: xampp\apache_start.bat), ou
  • putenv('https_proxy=http://127.0.0.1:8899'); antes de curl_init(), ou
  • curl_setopt($ch, CURLOPT_PROXY, '127.0.0.1:8899'); diretamente.

file_get_contents() não respeita nenhum desses — ele precisa de um stream context com 'proxy' => 'tcp://127.0.0.1:8899'.

Vinculação direta

Não é necessário proxy quando a ferramenta pode vincular seu próprio soquete:

FerramentaFlag
curl--interface <lanIp>
wget--bind-address=<lanIp>
npm--local-address=<lanIp>
ssh-b <lanIp>
Chromium / Playwrightnão existe flag de vinculação — use o MCP de navegador na porta 8900, veja abaixo

MCP de Navegador / Playwright

Chromium não tem flag de vinculação de origem, então um navegador atinge a ISP apenas através do proxy. Use lan-browser.mjs: é um servidor MCP Playwright que inicia e possui seu próprio proxy, para que o navegador nunca possa ser iniciado sem um.

Ele é fornecido com este skill — scripts/install.ps1 o registra:

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

Quais hosts usam a ISP vem de três fontes mescladas, então isso quase nunca requer reinstalação: lan-hosts.txt no diretório do skill (padrões fornecidos, substituídos no git pull), ~/.lan-direct-hosts (do próprio usuário, nunca sobrescrito), e --lan-hosts a,b na inicialização. Uma regra por linha, comentários # permitidos, correspondência por sufixo.

Quando um usuário desejar que outro host seja roteado via ISP, adicione uma linha a ~/.lan-direct-hosts e peça para ele reiniciar o navegador — não edite lan-hosts.txt, que a próxima atualização sobrescreverá.

Se o usuário pedir para registrá-lo manualmente, observe que -- deve sobreviver ao vinculador de parâmetros do PowerShell — splat os argumentos como um array, ou execute-o a partir do bash:

# a partir de um clone
claude mcp add playwright-split --scope user -- \
  node <skill-dir>/scripts/lan-browser.mjs --lan-hosts ".ir"

# a partir do npm — sobrevive à movimentação do checkout, então prefira quando o usuário o tiver instalado
claude mcp add playwright-split --scope user -- npx -y lan-direct browser

Outros subcomandos CLI, quando o pacote está instalado: lan-direct detect escolhe o script de detecção correto para a plataforma, e lan-direct route <host> responde "este host iria pela ISP ou pelo túnel?" sem iniciar um navegador. Recorra a este quando um usuário relatar que um site não carrega — uma regra errada e um site indisponível parecem idênticos externamente.

O servidor detecta o IP da LAN, recusa iniciar se lan-detect disser que o bypass está quebrado, reutiliza um proxy existente se já estiver ouvindo, e desliga o que iniciou quando o MCP é encerrado. Seus próprios logs vão para stderr; stdout é mantido limpo para JSON-RPC.

Ele usa a porta 8900, deliberadamente diferente da 8899 usada para testes de backend acima. A reutilização é apenas por porta, então compartilhar o número permitiria que um proxy de backend que vincula tudo capturasse silenciosamente o navegador e roteasse todo o seu tráfego pela ISP.

Navegação mista funciona em um único navegador. --lan-hosts faz o proxy decidir por host: hosts correspondentes saem pela ISP, todo o resto permanece não vinculado e segue o túnel. Assim, "verificar hoosha e youtube" não requer alternância de ferramentas — hoosha sai pela ISP, youtube pela VPN, mesma sessão. O log do proxy mostra a decisão em cada linha:

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

Mantenha a lista precisa: um host doméstico ausente dela recebe o túnel e falha com uma resposta vazia, e um host estrangeiro adicionado erroneamente recebe a ISP e pode ser filtrado. Quando um site falha de uma forma que parece uma conexão morta, verifique qual rota o log escolheu antes de assumir que o site está indisponível.

Por que uma lista explícita em vez de detecção automática: conectar-se a um host bloqueado através do túnel é bem-sucedido no nível TCP (medido: connect@14ms) e morre apenas ~860ms depois, após o proxy já ter respondido 200 Connection Established. Fazer fallback significaria armazenar em buffer e reproduzir o handshake TLS e pagar esse atraso em cada requisição doméstica.

Playwright roteia loopback através do proxy. O Chrome normalmente ignora localhost, mas o Playwright substitui isso quando um proxy está configurado, então http://127.0.0.1:3000 chega em lan-proxy.mjs em vez de ir direto. Verificado no log do proxy. O guardião de loopback do proxy é o que mantém os servidores de desenvolvimento locais acessíveis — sem ele, cada uma dessas requisições falha com EADDRNOTAVAIL. Não adicione --proxy-bypass=<-loopback>; isso não muda nada aqui e apenas confunde o quadro.

Solução de problemas

directEgress igual a lanEgress com um túnel ativo. A VPN está impondo um kill-switch ou rota estrita (um filtro WFP que bloqueia soquetes vinculados). Corrija no cliente VPN, não ao redor dele: desabilite rota estrita (strict route), ou adicione uma regra de roteamento direta/bypass para os hosts necessários. nekoray/sing-box: desligue strict_route, ou adicione uma regra de saída direct.

No Linux isso também pode acontecer sem kill-switch: um endereço de origem sozinho não substitui a tabela de roteamento lá como faz no Windows, então o roteamento por política mantém o tráfego no túnel. A correção é uma regra enviando tráfego do IP da LAN para fora pelo gateway físico — ip rule add from <lanIp> table <n> mais uma rota padrão nessa tabela. Isso requer root e é uma alteração em todo o sistema, então explique e deixe o usuário decidir. Todo o resto neste skill permanece sem privilégios e com escopo de processo.

fakeIpDns: true. O resolvedor do SO está retornando placeholders 198.18.x.x que só significam algo dentro do túnel. Passe --dns <lanDns> para o proxy para que ele resolva através do DNS da própria LAN.

Sonda de egresso não retorna nada. Nenhuma conectividade com o host da sonda. Verifique o link, execute novamente.

Válvula de escape

Se um backend genuinamente não puder ser proxyzado (um binário compilado sem suporte a proxy), a única opção restante é uma rota estática específica para o host:

route add <targetIp> mask 255.255.255.255 <gateway> metric 1   # precisa de admin
route delete <targetIp>                                        # desfazer, sempre

Isso é global. Puxa todo processo para fora do túnel para aquele destino — incluindo você, se você algum dia contatar o mesmo host. Requer elevação, sobrevive até ser deletado, e é fácil de esquecer. Pergunte antes de usá-lo, e delete-o na mesma sessão.

Habilidades Relacionadas