DEV Community

Cover image for 🔍 4 Befehle, mit denen ich prüfe, ob mein Claude-Code-Gateway wirklich läuft
boluo
boluo

Posted on

🔍 4 Befehle, mit denen ich prüfe, ob mein Claude-Code-Gateway wirklich läuft

🤖 AI-Hinweis: Beim Strukturieren und Formulieren dieses Beitrags habe ich ein Sprachmodell eingesetzt. Alle Befehle, Ausgaben und Versionsnummern habe ich auf meiner eigenen Maschine tatsächlich ausgeführt und überprüft.

TL;DR

  • Claude Code lässt sich auf ein eigenes Inference-Gateway umbiegen – es sind genau 6 Umgebungsvariablen.
  • claude --version beweist nur, dass der Client installiert ist. Nichts weiter. 🙃
  • Die beiden Base-URLs sind nicht symmetrisch: ANTHROPIC_BASE_URL ohne /v1, OPENAI_BASE_URL mit.
  • Das Panel in der Desktop-App ist standardmäßig unsichtbar – es steckt hinter dem Developer Mode.
  • Der eigentliche Test ist ein echter Request mit max_tokens=16. Alles davor ist Aufwärmen.
  • Nach der Konfiguration ein neues Terminal-Fenster. Im alten ist „command not found" das erwartete Ergebnis.

Gemessen habe ich alles auf:

Claude Code 2.1.283
Node.js v24.21.0
OS Windows 10 Pro 22H2 (build 19045)
Git nicht installiert

Die letzte Zeile ist kein Tippfehler. Dazu komme ich am Ende. 👇


Das Problem: „installiert" ist nicht „läuft"

Ich wollte die Inference-Requests von Claude Code auf ein anderes Gateway umleiten. Die Konfiguration selbst war unspektakulär – ein paar Umgebungsvariablen, fertig.

Gehangen hat es danach.

Ich tippe claude --version, bekomme eine saubere Versionsnummer zurück und denke: passt. 🎉

Nur beweist das genau eine Sache – dass der Client auf der Platte liegt. Ob der Endpoint erreichbar ist, ob der Key gültig ist, ob das Modell überhaupt antwortet? Davon ist nichts geprüft.

Das ist der Punkt, an dem die meisten Anleitungen aufhĂśren. Und genau da fangen die Probleme an.


0️⃣ Was hier technisch überhaupt passiert

Falls du das zum ersten Mal machst, kurz das Mentalmodell – ohne das sind die Fehlermeldungen später nicht lesbar.

Claude Code ist ein Client. Er muss zwei Dinge wissen: wohin der Request geht und womit er sich ausweist. Beides liest er beim Start aus dem Environment (oder, in der Desktop-App, aus den App-Settings). Dann baut er einen ganz normalen HTTPS-Request und schickt ihn los.

Mehr passiert nicht. Keine Magie, kein Daemon, keine Registrierung irgendwo.

Deshalb gibt es auch genau drei Stellen, an denen es schiefgehen kann:

  1. Der Wert steht nicht da, wo der Client hinschaut. → Er nimmt den Default oder gar nichts.
  2. Der Wert steht da, ist aber falsch formatiert. → Der Request geht raus und läuft ins Leere.
  3. Der Wert ist korrekt, aber die Gegenseite lehnt ab. → Key abgelaufen, Quota leer, Modell unbekannt.

Und jetzt der Clou: Die Befehle, die alle als Erstes tippen, prüfen ausschließlich Kategorie 1 – und nicht mal die vollständig. Kategorie 2 und 3 sind für sie unsichtbar.


1️⃣ Erst rausfinden, in welcher Situation du bist

claude --version
Enter fullscreen mode Exit fullscreen mode
Ausgabe Welches Skript
Versionsnummer kommt configure – schreibt nur die Config
Command not found setup – installiert erst die Dependencies

Gute Nachricht: die configure-Variante hat einen Hard-Check eingebaut. Sie sucht als Erstes claude im PATH und bricht sofort ab, wenn sie nichts findet. Falsch gewählt = nichts kaputt, das Skript verweigert einfach den Dienst. 👍

Umgekehrt gilt das nicht. setup auf einer schon eingerichteten Maschine installiert die Dependencies neu. Also: diesen Schritt nicht Ăźberspringen.


2️⃣ Skript laufen lassen

Die Installationsbefehle fĂźr beide Plattformen

Claude Code ist schon da – nur konfigurieren:

curl -fsSL https://raw.githubusercontent.com/xujfcn/crazyrouter-claude-code/main/configure.sh | bash
Enter fullscreen mode Exit fullscreen mode
irm https://raw.githubusercontent.com/xujfcn/crazyrouter-claude-code/main/windows/configure.ps1 | iex
Enter fullscreen mode Exit fullscreen mode

Noch gar nichts da? Im selben Repo-Verzeichnis liegt die Full-Install-Variante – einfach configure im Befehl durch setup ersetzen (setup.sh bzw. windows/setup.ps1). Die installiert erst Git, Node.js und Claude Code und schreibt dann die Config.

Umgekehrt bitte nicht: setup auf einer schon eingerichteten Maschine installiert die Dependencies neu.

⚠️ Öffne die Links vorher im Browser und lies drüber. Das hat nichts damit zu tun, wer das Skript geschrieben hat – bei allem in der Form | bash oder | iex gehört das dazu. Die Details unten habe ich genau so aus dem Quellcode gelesen.

Das Skript fragt genau drei Dinge

Frage Pflicht Verhalten
Token ja Die Eingabe wird nicht angezeigt. Nach dem Einfügen bleibt der Bildschirm leer – das ist normal, nicht dreimal reinpasten 😅 Leer lassen = Fehler und Abbruch
Base URL nein Default vorhanden, entfernt einen ßberzähligen Slash am Ende automatisch
Modell nein Default vorhanden, später jederzeit änderbar

Zwei Dinge, die man nur durchs Lesen des Quellcodes erfährt:

  • Beim Token gibt es einen Format-Precheck (Prefix sk- / cr- / rk-), aber ein Mismatch erzeugt nur eine Warnung und blockt nicht. Warning gesehen? Erst weiterlesen, ob danach ein echter Fehler kommt.
  • Die Skript-Ausgabe ist auf Englisch. Ein Bildschirm voll Englisch ist also erwartbar.

Was passiert, wenn man mittendrin abbricht?

Habe ich getestet, weil ich es wissen wollte. Ich habe das Skript nicht-interaktiv laufen lassen: Es lädt sauber runter, meldet [OK] Claude Code detected: 2.1.283, kommt zum Token-Prompt – und steigt dort mit Exit-Code 1 aus, weil in einem nicht-interaktiven Subprozess nichts zu lesen ist.

Danach habe ich alle sechs Variablen einzeln kontrolliert: keine einzige verändert. 🙌

Das Skript sammelt also erst alle drei Antworten und schreibt dann. Ein Abbruch hinterlässt keine halbe Config. Praktisch heißt das: Wenn etwas schiefgeht, einfach nochmal starten – kein manuelles Aufräumen nötig.

Unattended auf dem Server

Wenn du nicht interaktiv sein kannst, Token vorher ins Environment:

export CRAZYROUTER_TOKEN="dein-token"
curl -fsSL https://raw.githubusercontent.com/xujfcn/crazyrouter-claude-code/main/configure.sh | bash
Enter fullscreen mode Exit fullscreen mode

(Bare-Metal-Server ohne Claude Code? Auch hier configure → setup.)

Ein Detail, das dieses Skript richtig macht: curl | bash läuft üblicherweise in einer Non-Login-Shell, und die kennt die npm-Global-Pfade oft nicht. Das Skript sucht deshalb aktiv in /usr/local/bin, ~/.local/bin und ~/.npm-global/bin nach claude. Und selbst wenn es nichts findet, schreibt es die Config trotzdem fertig und gibt Diagnose-Output aus – statt auf halber Strecke zu sterben.

Noch ein Wort zum Windows-setup

Weil das der Teil ist, der auf älteren Maschinen entscheidet, ob es klappt: Das Skript versucht zuerst winget. Ist kein winget da, fällt es auf Direct Download zurück (Node.js über einen fixierten LTS-Installer, Git über das offizielle Release). Und für PowerShell 5.1 aktiviert es explizit TLS 1.2 und 1.3 – ohne das scheitern die Downloads auf unpatchten Systemen mit einer Fehlermeldung, die überhaupt nicht nach TLS aussieht.


3️⃣ Nachsehen, was tatsächlich geschrieben wurde

Sechs Umgebungsvariablen auf User-Ebene:

ANTHROPIC_BASE_URL     Gateway-Adresse (OHNE /v1)
ANTHROPIC_AUTH_TOKEN   Token
ANTHROPIC_MODEL        Default-Modell
CLAUDE_MODEL           Default-Modell
OPENAI_API_KEY         derselbe Token
OPENAI_BASE_URL        Gateway-Adresse (MIT /v1)
Enter fullscreen mode Exit fullscreen mode

Warum zwei Sets? Claude Code liest die Anthropic-Konvention, viele OpenAI-kompatible Tools die andere. Ein Token deckt beide Seiten ab.

Und hier kommt die Falle, die mich am meisten gekostet hat: die beiden Adressen sind nicht symmetrisch. Bei ANTHROPIC_BASE_URL gehört nur die Root-Domain rein – der Client baut /v1/messages selbst dran. Wer das /v1 manuell ergänzt, landet bei /v1/v1/messages – und das ist falsch. Ein Gateway, das strikt nach Pfad routet, antwortet darauf mit 404. Darauf verlassen kann man sich aber nicht: mein eigenes habe ich nachgemessen, es liefert auf diesen Pfad weiterhin 200. Der zweite Fall ist der unangenehmere, weil auf dem Bildschirm gar kein Fehler auftaucht.

OPENAI_BASE_URL braucht das /v1 dagegen zwingend.

Das Perfide daran: In diese Falle tappen ausgerechnet die ordentlichen Leute. Zwei Felder zeigen auf denselben Server, also bringt man sie in dieselbe Form. Der Instinkt ist völlig richtig – der Fall ist nur asymmetrisch. Es gibt keinen Linter dafür, weil syntaktisch beide Werte einwandfrei sind. Ergebnis: Claude Code läuft, die anderen Tools kommen nicht durch – meist mit 404, aber was genau zurückkommt, hängt vom Gateway ab. Oder umgekehrt. 🤦

Wo die Werte landen

Unter Windows direkt als User-Umgebungsvariablen. Unter macOS/Linux in einer env-Datei im Home-Verzeichnis plus eine Load-Zeile in der Shell-Startdatei.

Nettes Detail: die Startdatei wird nach der echten Login-Shell ausgewählt, nicht danach, welche Datei existiert. Auf Servern liegt gerne mal eine ungenutzte .bashrc rum, während der Login tatsächlich über zsh läuft – wer dort reinschreibt, schreibt ins Nichts. echo $SHELL, bevor du irgendwas anfasst.

Alternative: pro Projekt

Wer das System-Environment nicht anfassen will, legt eine .claude/settings.json ins Projekt-Root:

{
  "env": {
    "ANTHROPIC_BASE_URL": "https://api.crazyrouter.com",
    "ANTHROPIC_AUTH_TOKEN": "sk-dein-token",
    "ANTHROPIC_MODEL": "claude-opus-4-8"
  }
}
Enter fullscreen mode Exit fullscreen mode

Gilt nur für dieses Projekt – super, wenn verschiedene Repos über verschiedene Gateways laufen sollen. Aber: Das ist genauso Klartext wie die Umgebungsvariablen, nur deutlich leichter versehentlich zu committen. Wenn du diesen Weg gehst, sofort in die .gitignore. 🔐

Die drei Wege im Vergleich

Skript Desktop-GUI settings.json
Scope ganze Maschine nur Desktop-App nur dieses Projekt
URL-Normalisierung ja (Trailing Slash weg) nein nein
Gut fßr neue Maschine, Server, Wiederholbarkeit Einzelplatz, gelegentlich ändern mehrere Gateways parallel
Commit-Risiko niedrig niedrig hoch ⚠️

4️⃣ Desktop-App: das versteckte Panel

Mit der Desktop-App brauchst du die Umgebungsvariablen nicht anzufassen. Aber das Panel ist standardmäßig nicht sichtbar. Es hängt unter dem Developer Mode.

Help → Troubleshooting → Enable Developer Mode
Enter fullscreen mode Exit fullscreen mode

Danach startet die App einmal neu, und erst dann erscheint im MenĂź oben links ein Eintrag Developer:

Der Developer-Eintrag nach Aktivierung des Developer Mode

Darunter dann Configure third-party inference:

Der Einstieg zur Third-Party-Inference-Konfiguration

Links Connection wählen. So sieht es bei mir konfiguriert aus:

Das Gateway-Konfigurationspanel

ℹ️ Fairness-Hinweis: Auf meiner Maschine war der Developer Mode von vorher schon aktiv. Dass das Panel ohne ihn nicht sichtbar ist, habe ich aus der offiziellen Doku – selbst nachgestellt habe ich es nicht. Den Menüpfad gebe ich als dokumentiert weiter, nicht als reproduziert.

Feld fĂźr Feld:

  • Credential kind → Static API key. Bedeutet: Credential-Quelle festnageln. Danach wird nicht mehr auf Login-Status oder Umgebungsvariablen zurĂźckgefallen. FĂźr das Debugging ist das wertvoll, weil es das Szenario „ich dachte, ich gehe Ăźber mein Gateway, in Wahrheit bin ich auf den Default zurĂźckgefallen" komplett ausschließt. Kehrseite: Wenn Requests trotz Konfiguration weiter zur alten Adresse gehen – hier zuerst schauen.
  • Gateway base URL → Das GUI räumt das Format nicht auf, anders als das Skript. Kein Slash am Ende, kein selbst ergänztes /v1, keine Query-Parameter.
  • Gateway API key → Nach dem EinfĂźgen aufs Auge-Icon klicken und drĂźberschauen. Aus dem Browser kopierte Keys schleppen gerne ein unsichtbares Leerzeichen mit, und der Fehler sieht danach genau wie „Key ungĂźltig" aus.
  • Gateway auth scheme → bearer oder x-api-key. Bestimmt, ob als Authorization: Bearer xxx oder als x-api-key: xxx gesendet wird.
  • Artifact preview iframe origin → leer lassen, wenn du keinen speziellen Bedarf hast.

Reihenfolge der Buttons beachten: zuerst oben rechts Test connection, und erst wenn das durch ist, unten rechts Apply Changes. Der Test läuft auch auf noch nicht angewendeten Werten durch – „getestet und Fenster zu" ist also eine zuverlässige Methode, nichts zu speichern. 💾

Noch eine Debug-Richtung: links gibt es außerdem Sandbox & workspace und Egress. Die Desktop-App sandboxt Tool-Traffic. Wenn alle Felder stimmen und Test connection trotzdem scheitert, lohnt ein Blick in die Liste der erlaubten Egress-Hosts. Bei mir ist das nie aufgetreten (die aktuelle Config geht direkt durch) – ich notiere es nur als Richtung, nicht als reproduzierten Fall.


5️⃣ Die vier Befehle 🎯

Die Ausgabe der vier PrĂźfbefehle

Eins – ist der Client da?

claude --version
Enter fullscreen mode Exit fullscreen mode

Beweist: Im PATH liegt eine ausfĂźhrbare Datei. Das ist der komplette Geltungsbereich. Bei mir: 2.1.283

Zwei – ist die Runtime da?

node --version
Enter fullscreen mode Exit fullscreen mode

Beweist: Node.js ist installiert. Auch schon alles. Bei mir: v24.21.0

Drei – ist die Config wirklich geschrieben? Nicht „ich habe sie eingegeben", sondern „ich habe sie zurückgelesen".

env | grep -E 'ANTHROPIC|OPENAI'
Enter fullscreen mode Exit fullscreen mode
'ANTHROPIC_BASE_URL','ANTHROPIC_AUTH_TOKEN','ANTHROPIC_MODEL','CLAUDE_MODEL','OPENAI_API_KEY','OPENAI_BASE_URL' |
  ForEach-Object { "{0,-22} {1}" -f $_, [Environment]::GetEnvironmentVariable($_,'User') }
Enter fullscreen mode Exit fullscreen mode

Zwei Dinge prĂźfen: alle sechs vorhanden, und /v1 nur bei OPENAI_BASE_URL.

Vier – Endpoint, Key und Modell gleichzeitig ⬅️ der eigentliche Test

curl -s https://api.crazyrouter.com/v1/messages \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \
  -H "anthropic-version: 2023-06-01" \
  -d '{
    "model": "claude-opus-4-8",
    "max_tokens": 16,
    "messages": [{"role": "user", "content": "reply with: pong"}]
  }'
Enter fullscreen mode Exit fullscreen mode

max_tokens: 16 ist Absicht: Der Inhalt der Antwort ist irrelevant, relevant ist nur, dass es eine gibt. Eine erfolgreiche Antwort etabliert drei Fakten gleichzeitig – Endpoint erreichbar, Key akzeptiert, Modell antwortet.

Warum die ersten drei genau dort blind sind, wo es zählt

Alle drei inspizieren den Zustand deiner lokalen Maschine. Keiner von ihnen redet mit dem Gateway.

Diese Failure Modes sind fĂźr sie also komplett unsichtbar:

  • 🔑 Key abgelaufen
  • 📉 Quota leer
  • 🏷️ Modell-ID wird vom Gateway nicht erkannt
  • 🌐 Proxy oder VPN routet die Domain woandershin
  • 🚧 Gateway-Domain nicht in der Egress-Whitelist

Alles davon geht durch Befehl 1–3 ohne eine einzige Warnung und fällt erst bei Nummer 4 um.

Dass die ersten drei durchgehen und der vierte scheitert, ist also nicht der Randfall – es ist die Normalform dieses Fehlers. Drei Viertel der üblichen Prüfung sind strukturell unfähig, das zu finden, was am wahrscheinlichsten kaputt ist. Wenn du nur einen Befehl machst: mach den vierten.

Zum Auth-Header noch: ich habe mit beiden Varianten je einen echten Request geschickt. Auf diesem Gateway antworten bearer und x-api-key mit 200. Das ist aber keine allgemeine Regel – die meisten Gateways akzeptieren nur eines, und die Fehlermeldung bei der falschen Wahl ist von „Key kaputt" praktisch nicht zu unterscheiden. Doku des Anbieters lesen.


🧰 Fehlermeldungen, die ich gesehen habe (und was sie wirklich bedeuten)

„command not found" direkt nach erfolgreicher Konfiguration

Umgebungsvariablen greifen nur für neu gestartete Prozesse. Im alten Fenster zu prüfen scheitert immer, und das ist das erwartete Ergebnis, keine Fehlkonfiguration. Fenster komplett schließen; ein neuer Tab reicht unter Windows nicht. Die erste Next-Step-Zeile des Skripts ist übrigens genau Open a NEW PowerShell window – geht in der englischen Ausgabe nur leicht unter.

„no token provided" – obwohl der Token gesetzt ist

Das hat mich am meisten Zeit gekostet, und ich war schon dabei, einen neuen Key auszustellen. Der Key war vĂśllig in Ordnung. Die PrĂźfung lief in einem Subprozess, und in dessen Environment war $env:ANTHROPIC_AUTH_TOKEN einfach leer.

Aus Serversicht sind die zwei Fälle nicht unterscheidbar: Es kam ein Request ohne Key an. Woher soll der Server wissen, ob du keinen hast oder ob er unterwegs verloren ging? Die Meldung war präzise und schlimmer als nutzlos, weil sie mit voller Überzeugung auf die falsche Hypothese zeigte.

In PowerShell lieber explizit aus der User-Scope lesen statt auf $env: zu vertrauen:

[Environment]::GetEnvironmentVariable('ANTHROPIC_AUTH_TOKEN','User')
Enter fullscreen mode Exit fullscreen mode

404, obwohl die URL richtig aussieht

/v1 doppelt. Siehe oben. Das ist die häufigste Ausprägung der Asymmetrie.

Validierungsfehler bei der Endpoint-Adresse

Aus einem Share-Link kopiert? Dann hängt am Ende vermutlich ein ?utm_source=.... Im Browser egal, als API-Endpoint bestenfalls ignoriert, schlimmstenfalls Validierungsfehler.

Git-Installation scheitert – alles hinwerfen?

Nein. Auf dieser Maschine ist Git ßberhaupt nicht installiert, trotzdem läuft claude und trotzdem geht Befehl vier durch. Git ist keine Runtime-Dependency von Claude Code. Das Full-Setup-Skript installiert es fßr die Arbeit danach, nicht als Voraussetzung.

Und weil das die zweite Stelle war, an der ich mir selbst reingefallen bin: Ich hatte „kein Git" kurzzeitig als Ursache meines Problems abgehakt und angefangen, von dieser Annahme aus zu debuggen. Genau derselbe Fehler wie mit der Versionsnummer – ich habe einen Zustand erschlossen, den ich nicht beobachtet hatte, und bin dann selbstsicher weitergelaufen.


Takeaways

Die drei Dinge, die ich mir gemerkt habe:

  1. ANTHROPIC_BASE_URL ohne /v1, OPENAI_BASE_URL mit. Vereinheitlichen ist falsch – und gibt nicht zwangsläufig einen 404.
  2. Nach der Konfiguration ein neues Terminal-Fenster.
  3. Endpoint-Adressen müssen sauber sein – keine Query-Parameter aus kopierten Links.

Und das Ăźbergeordnete: Die Disziplin liegt nicht in den Checks, sondern in der Bereitschaft, laut zu sagen, was ein Signal eigentlich beweist. Nicht worauf es hindeutet, sondern was es etabliert. Eine Versionsnummer etabliert, dass eine Datei auf der Platte liegt. Ein grĂźnes Dashboard etabliert, dass ein Check gelaufen ist und unter einem Threshold blieb. GrĂźne Tests etablieren, dass die Assertions erfĂźllt sind, die du geschrieben hast.

Keins davon heißt „läuft". Alle sind deutlich billiger zu bekommen als „läuft" – deshalb sammeln sie sich an, und deshalb lassen wir sie das Ergebnis ersetzen.

Und zur Sicherheit: Token nicht ins Git committen, nicht in Screenshots reinrutschen lassen, beim Maschinenwechsel den alten Key widerrufen. Klingt banal – mir ist aber genau während dieses Debuggings aufgefallen, wie oft mein Key einfach im Klartext auf meinem eigenen Bildschirm lag. 🔐


Und bei euch? 💬

Mich interessiert vor allem eines: welcher der vier Befehle ist bei euch zuletzt gescheitert?

Ich habe den Verdacht, dass Nummer vier bei fast allen der Stolperstein ist – aber aus unterschiedlichen Gründen. Falsches Auth-Scheme? Proxy dazwischen? Modell-ID, die das Gateway nicht kennt? Schreibt es in die Kommentare, ich ergänze gern eine Fehlerliste.

Top comments (0)