Communitygithub.com

farshadmomo/lan-direct

Fertigkeit + Browser-MCP für Claude Code und Codex: App und Browser erreichen lokale Dienste über den echten ISP, während der Agent im VPN bleibt. Pro-Socket-Bindung, Pro-Host-Routing, kein Admin, nichts global.

Was ist lan-direct?

Diese Fertigkeit (Skill) für KI-Agenten wie Claude Code und Codex ermöglicht es, dass die von diesen Agenten gestarteten Anwendungen und Browser den normalen Internetanschluss (ISP) des Rechners nutzen, während der Agent selbst weiterhin über eine VPN-Verbindung läuft. Der Kernmechanismus ist die Bindung einzelner Netzwerk-Sockets auf Prozessebene, wodurch keine Administratorrechte benötigt werden und die systemweiten Routing-Einstellungen oder die VPN-Konfiguration unberührt bleiben. Der Skill umfasst Erkennungsskripte (für Windows & Unix), einen Proxy-Server und ein spezielles Modul für Node.js-Anwendungen. Er bietet zudem einen Browser-MCP-Server, der es ermöglicht, Webseiten gemischt zu nutzen: definierte Hosts (z. B. lokale oder heimische Dienste) werden über den ISP, alle anderen über das VPN aufgerufen. Die Konfiguration erfolgt über Umgebungsvariablen oder Startparameter, der gesamte Vorgang ist rückstandslos umkehrbar. Der Skill löst typische Probleme, wenn ein Agent im VPN arbeitet und gleichzeitig auf Dienste zugreifen muss, die nicht über das VPN verfügbar sind, ohne die Verbindung des Agenten zu unterbrechen oder systemweit Änderungen vorzunehmen.

Funktioniert mit✓Claude Code✓Codex CLI~Cursor
npx skills add farshadmomo/lan-direct

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Was macht lan-direct?

Leiten Sie den Datenverkehr der App über den ISP, während Sie als Agent im VPN bleiben.

Funktioniert sowohl in Claude Code als auch in Codex – der Mechanismus ist die Pro-Socket-Bindung, die nichts mit der Nutzeroberfläche zu tun hat.

Die Invariante

Ändern Sie niemals die Routingtabelle, die WinINET/System-Proxy-Einstellungen oder die Konfiguration des VPN-Clients. Nur:

  • Setzen Sie Umgebungsvariablen, die auf einen Kindprozess beschränkt sind, oder
  • übergeben Sie Per-Tool-Quellbindungs-Flags.

Beides ist Pro-Socket und Opt-In, sodass Ihre eigene Verbindung nicht beeinträchtigt werden kann. Das ist der Sinn dieser Fertigkeit – wenn Sie zu route add greifen, haben Sie sie gebrochen (siehe Abschnitt Notluke am Ende).

Schlagen Sie nicht vor, die VPN zu trennen. Das ist das Problem, das diese Fertigkeit vermeidet.

Arbeitsablauf

1. Erkennen und überprüfen

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

Unter Linux oder macOS verwenden Sie stattdessen das Gegenstück – gleiches JSON, gleicher Exit-Code-Vertrag:

bash scripts/lan-detect.sh

Gibt JSON aus: lanIp, gateway, lanDns, subnet, tunnelActive, vpnAdapter, directEgress, lanEgress, fakeIpDns, bypassWorks. Beendet sich nur mit Exit-Code 0, wenn bypassWorks wahr ist – es beweist die Umgehung mit zwei Live-Probes, anstatt sie nur zu vermuten.

  • tunnelActive: false -> Kein VPN ist aktiv. Sagen Sie es und stoppen Sie; es gibt nichts zu umgehen.
  • bypassWorks: false bei aktivem Tunnel -> Gehen Sie zu Fehlerbehebung unten. Fahren Sie nicht fort. Der Datenverkehr würde stillschweigend im VPN bleiben und Sie würden einen falschen Erfolg melden.

2. Mechanismus auswählen

Node-Backend (inkl. Next.js)? Überspringen Sie den Proxy – springen Sie zum Node-Rezept unter Rezepte pro Stack. Eine Umgebungsvariable, kein Proxy-Prozess, und es ist die einzige Möglichkeit, die natives fetch erreicht.

Alles andere: Starten Sie den LAN-gebundenen Proxy. Ein Serverprozess hat kein --interface-Flag, daher ist dies der Weg, ihm einen ISP-Ausgang zu geben.

node scripts/lan-proxy.mjs --bind <lanIp> --port 8899          # im Hintergrund ausführen
node scripts/lan-proxy.mjs --bind <lanIp> --port 8899 --dns <lanDns>   # wenn fakeIpDns: true

Es lauscht nur auf 127.0.0.1 und protokolliert jeden Host, den es tunnelt, sodass Sie sehen können, was die App tatsächlich aufgerufen hat.

3. Backend mit Proxy-Adresse neu starten

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

NO_PROXY ist wichtig: Ohne sie werden die Aufrufe des Backends an seine eigenen lokalen Dienste grundlos durch den Proxy geleitet. (Lokale DBs über TCP – MySQL, Postgres, Mongo – sind nicht HTTP und werden ohnehin nicht beeinflusst.)

Setzen Sie diese beim Startbefehl des Backends, nicht global. Siehe Rezepte pro Stack unten – HTTP_PROXY allein reicht für mehrere Stacks nicht aus.

4. Testen und bestätigen

Rufen Sie den Endpunkt auf, dessen ausgehender Aufruf fehlschlug. Bestätigen Sie beides:

  • der Aufruf ist jetzt erfolgreich, und
  • das Proxy-Protokoll zeigt den Host, den es angewählt hat.

Ein Erfolg ohne passende Protokollzeile bedeutet, dass die Anfrage nie durch den Proxy gegangen ist.

5. Abbau

Stoppen Sie den Proxy, starten Sie das Backend ohne die Umgebungsvariablen neu. Nichts weiter rückgängig zu machen – es wurde kein globaler Zustand geändert.

pkill -f lan-proxy aus Git Bash tötet nicht eine Windows-node.exe. Verwenden Sie:

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

Eine übrig gebliebene Instanz ist harmlos, führt aber beim nächsten Start zu einem Portkonflikt.

Rezepte pro Stack

StackWie man den Proxy übergibt
Python requests / httpxUmgebungsvariablen, wie sie sind
Go, .NET HttpClientUmgebungsvariablen, wie sie sind
PHP CLIlibcurl liest http_proxy / https_proxy aus der Prozessumgebung
Node (inkl. Next.js)Nicht der Proxy – verwenden Sie das lan-bind.mjs-Preload, siehe unten
PHP unter Apache/XAMPPsiehe unten – der Dienst erbt Ihre Shell nicht
curl, wget, npm, ssh, gitÜberspringen Sie den Proxy, binden Sie direkt (siehe Direkte Bindung unten)

Node – Preload verwenden, nicht den Proxy

Natives fetch läuft auf Node's eingebautem undici, das HTTP_PROXY unter Node <= 23 ignoriert und kein https.Agent akzeptiert. Bestätigt unter Node 22.20: einfache Umgebungsvariablen, NODE_USE_ENV_PROXY=1 und --use-env-proxy tun alle nichts – das Proxy-Protokoll bleibt leer und der Datenverkehr geht stillschweigend über das VPN. Dies betrifft Next.js, das openai-SDK und alles andere auf nativem fetch.

Überspringen Sie den Proxy vollständig und verwenden Sie das Preload, das jeden ausgehenden Socket im Prozess bindet:

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

<skill-verzeichnis> ist das eigene Basisverzeichnis dieser Fertigkeit mit Vorwärtsschrägstrichen – normalerweise C:/Users/<ihr_name>/.claude/skills/lan-direct unter Claude Code, oder C:/Users/<ihr_name>/.agents/skills/lan-direct unter Codex. --import benötigt unter Windows eine absolute file://-URL; ein reiner Windows-Pfad mit Rückwärtsschrägstrichen schlägt fehl.

NODE_OPTIONS propagiert an geforkte Worker, daher deckt es Next.js- und Vite-Entwicklungsserver ab.

Dies erreicht natives fetch, das openai-SDK, axios, node-fetch, got und einfaches http/https auf einmal – keine Abhängigkeit, kein Proxy-Prozess, kein HTTP_PROXY nötig. Es überspringt Loopback-Ziele, sodass eine lokale DB oder ein Entwicklungsserver weiterhin funktioniert.

Nur falls das Preload nicht verfügbar ist: Node >= 24 beachtet NODE_USE_ENV_PROXY=1 mit den Umgebungsvariablen, und axios beachtet sie in jeder Version.

PHP unter Apache / XAMPP

SetEnv/PassEnv in httpd.conf füllt nur $_SERVER; libcurl liest die Prozessumgebung, daher wird es sie nicht aufnehmen. Verwenden Sie eine der folgenden Methoden:

  • Starten Sie Apache aus einer Shell, die bereits http_proxy/https_proxy exportiert hat (z.B. xampp\apache_start.bat), oder
  • putenv('https_proxy=http://127.0.0.1:8899'); vor curl_init(), oder
  • curl_setopt($ch, CURLOPT_PROXY, '127.0.0.1:8899'); direkt.

file_get_contents() beachtet keine davon – es benötigt einen Stream-Kontext mit 'proxy' => 'tcp://127.0.0.1:8899'.

Direkte Bindung

Kein Proxy nötig, wenn das Tool seinen eigenen Socket binden kann:

ToolFlag
curl--interface <lanIp>
wget--bind-address=<lanIp>
npm--local-address=<lanIp>
ssh-b <lanIp>
Chromium / Playwrightkein Bind-Flag vorhanden – verwenden Sie den Browser-MCP auf Port 8900, siehe unten

Browser / Playwright MCP

Chromium hat kein Quellbindungs-Flag, daher erreicht ein Browser den ISP nur über den Proxy. Verwenden Sie lan-browser.mjs: Es ist ein Playwright-MCP-Server, der seinen eigenen Proxy startet und besitzt, sodass der Browser nie ohne einen solchen starten kann.

Es wird mit dieser Fertigkeit ausgeliefert – scripts/install.ps1 registriert es:

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

Welche Hosts den ISP verwenden, stammt aus drei zusammengeführten Quellen, sodass dies fast nie eine Neuinstallation erfordert: lan-hosts.txt im Fertigkeitsverzeichnis (ausgelieferte Standardwerte, bei git pull ersetzt), ~/.lan-direct-hosts (die eigenen des Benutzers, niemals überschrieben) und --lan-hosts a,b beim Start. Eine Regel pro Zeile, #-Kommentare erlaubt, Suffix-Übereinstimmung.

Wenn ein Benutzer wünscht, dass ein anderer Host über den ISP geroutet wird, fügen Sie eine Zeile zu ~/.lan-direct-hosts hinzu und sagen Sie ihm, er solle den Browser neu starten – bearbeiten Sie nicht lan-hosts.txt, das beim nächsten Update überschrieben wird.

Wenn der Benutzer fragt, es von Hand zu registrieren, beachten Sie, dass -- den Parameterbinder von PowerShell überleben muss – übergeben Sie die Argumente als Array oder führen Sie es von bash aus:

# aus einem Klon
claude mcp add playwright-split --scope user -- \
  node <skill-verzeichnis>/scripts/lan-browser.mjs --lan-hosts ".ir"

# von npm – überlebt das Verschieben des Checkouts, daher vorziehen, wenn der Benutzer es installiert hat
claude mcp add playwright-split --scope user -- npx -y lan-direct browser

Andere CLI-Unterbefehle, wenn das Paket installiert ist: lan-direct detect wählt das richtige Erkennungsskript für die Plattform, und lan-direct route <host> beantwortet „würde dieser Host über den ISP oder den Tunnel gehen?", ohne einen Browser zu starten. Greifen Sie darauf zurück, wenn ein Benutzer meldet, dass eine Seite nicht lädt – eine falsche Regel und eine nicht erreichbare Seite sehen von außen identisch aus.

Der Server erkennt die LAN-IP, verweigert den Start, wenn lan-detect sagt, dass die Umgehung defekt ist, verwendet einen vorhandenen Proxy wieder, falls bereits einer lauscht, und fährt herunter, was er gestartet hat, wenn der MCP beendet wird. Seine eigenen Protokolle gehen nach stderr; stdout bleibt für JSON-RPC sauber.

Er verwendet Port 8900, bewusst nicht den Port 8899, der oben für Backend-Tests verwendet wurde. Die Wiederverwendung erfolgt allein nach Port, sodass das Teilen der Nummer dazu führen würde, dass ein Alles-bindendes Backend-Proxy den Browser stillschweigend einfängt und seinen gesamten Datenverkehr über den ISP leitet.

Gemischtes Browsen funktioniert in einem Browser. --lan-hosts lässt den Proxy pro Host entscheiden: übereinstimmende Hosts verlassen über den ISP, alles andere bleibt ungebunden und nimmt den Tunnel. „Überprüfe hoosha und youtube" erfordert also kein Tool-Wechsel – hoosha geht über den ISP, youtube über das VPN, dieselbe Sitzung. Das Proxy-Protokoll zeigt die Entscheidung in jeder Zeile:

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

Halten Sie die Liste genau: Ein fehlender lokaler Host erhält den Tunnel und schlägt mit einer leeren Antwort fehl, und ein fälschlicherweise hinzugefügter ausländischer Host erhält den ISP und wird möglicherweise gefiltert. Wenn eine Seite auf eine Weise ausfällt, die wie eine tote Verbindung aussieht, überprüfen Sie, welche Route das Protokoll gewählt hat, bevor Sie annehmen, dass die Seite down ist.

Warum eine explizite Liste und keine automatische Erkennung: Das Verbinden mit einem blockierten Host durch den Tunnel gelingt auf TCP-Ebene (gemessen: connect@14ms) und stirbt erst ~860ms später, nachdem der Proxy bereits mit 200 Connection Established geantwortet hat. Ein Fallback würde bedeuten, den TLS-Handshake zu puffern und wiederzugeben und diese Verzögerung bei jeder lokalen Anfrage zu bezahlen.

Playwright routet Loopback durch den Proxy. Chrome umgeht normalerweise localhost, aber Playwright überschreibt dies, wenn ein Proxy gesetzt ist, sodass http://127.0.0.1:3000 bei lan-proxy.mjs ankommt, anstatt direkt zu gehen. Bestätigt im Proxy-Protokoll. Die Loopback-Sperre des Proxys ist das, was lokale Entwicklungsserver erreichbar hält – ohne sie würde jede dieser Anfragen mit EADDRNOTAVAIL fehlschlagen. Fügen Sie kein --proxy-bypass=<-loopback> hinzu; es ändert hier nichts und verwirrt nur das Bild.

Fehlerbehebung

directEgress entspricht lanEgress bei aktivem Tunnel. Das VPN erzwingt eine Kill-Switch oder strikte Route (ein WFP-Filter, der gebundene Sockets blockiert). Beheben Sie es im VPN-Client, nicht drum herum: Deaktivieren Sie die strikte Route oder fügen Sie eine Direkt-/Bypass-Routingregel für die benötigten Hosts hinzu. nekoray/sing-box: Schalten Sie strict_route aus oder fügen Sie eine direct-Ausgangsregel hinzu.

Unter Linux kann dies auch ohne Kill-Switch passieren: Eine Quelladresse allein überschreibt dort nicht die Routingtabelle wie unter Windows, sodass Policy-Routing den Datenverkehr im Tunnel hält. Die Lösung ist eine Regel, die Datenverkehr von der LAN-IP über das physische Gateway sendet – ip rule add from <lanIp> table <n> plus eine Standardroute in dieser Tabelle. Dies erfordert Root und ist eine systemweite Änderung, erklären Sie es also und lassen Sie den Benutzer entscheiden. Alles andere in dieser Fertigkeit bleibt unprivilegiert und prozessbezogen.

fakeIpDns: true. Der OS-Resolver gibt 198.18.x.x-Platzhalter zurück, die nur innerhalb des Tunnels eine Bedeutung haben. Übergeben Sie --dns <lanDns> an den Proxy, damit er über das eigene DNS des LANs auflöst.

Ausgangsprobe gibt nichts zurück. Keine Konnektivität zum Probe-Host. Überprüfen Sie die Verbindung, führen Sie es erneut aus.

Notluke

Wenn ein Backend wirklich nicht proxyt werden kann (eine kompilierte Binärdatei ohne Proxy-Unterstützung), ist die einzig verbleibende Option eine host-spezifische statische Route:

route add <zielIp> mask 255.255.255.255 <gateway> metric 1   # benötigt Admin
route delete <zielIp>                                        # rückgängig machen, immer

Dies ist global. Es zieht alle Prozesse für dieses Ziel aus dem Tunnel – einschließlich Ihnen, wenn Sie jemals denselben Host kontaktieren. Erfordert erhöhte Rechte, bleibt bestehen, bis es gelöscht wird, und wird leicht vergessen. Fragen Sie vor der Verwendung, und löschen Sie es in derselben Sitzung.

Verwandte Skills