Multi-User Chat-Oberfläche für lokale Claude-Code-Sessions über das Channels-Feature von Claude Code – nach dem Vorbild des offiziellen Telegram-Plugins.
Du startest claude lokal selbst (mit dem oa-shell-Channel); die Web-App ist nur die
Oberfläche: Login, Chat, Tool-Freigaben und ein Datei-Browser des Session-Arbeits
verzeichnisses. Die App hostet keine Sessions, verwaltet keine Verzeichnisse und
speichert keine Claude-API-Keys.
[ deine Maschine ] [ Server: oa-shell ]
claude (interaktiv, dein Verzeichnis) Spring-Boot-Web-App
│ stdio (MCP) (Login, Chat, Freigaben, Datei-Browser)
oa-shell-Channel ──── ausgehende WSS (Bearer) ────► /bridge
Browser ──── Login / Chat / Freigabe / Dateien ────► Web-UI
Server (Web-App): JDK 21, Maven ≥ 3.6.3, ein Google-OAuth-Client.
Deine Maschine (Channel): Node ≥ 18 und claude ≥ 2.1.81 mit für deinen Account
freigeschaltetem Channels-Research-Preview (Auth über claude.ai-Login oder einen
Anthropic-Console-API-Key; nicht auf Bedrock/Vertex/Foundry).
In der Google Cloud Console einen OAuth-2.0-Client (Typ „Web") anlegen:
- Autorisierte Redirect-URI:
http://localhost:8080/login/oauth2/code/google(bzw.https://DEINE-DOMAIN/login/oauth2/code/googlein Produktion) - Client-ID und Secret kopieren.
cd app
export JAVA_HOME="$(brew --prefix openjdk@21)" # macOS; sonst auf ein JDK 21 zeigen
export GOOGLE_CLIENT_ID="…"
export GOOGLE_CLIENT_SECRET="…"
mvn spring-boot:run # Entwicklung
# ODER als Jar:
mvn -DskipTests package
java -jar target/oa-shell-app-0.0.1-SNAPSHOT.jarDie App läuft auf http://localhost:8080 (Port mit --server.port=9090 überschreibbar,
falls 8080 belegt ist). Öffne die Seite und melde dich mit „Mit Google anmelden" an.
Produktion: Hinter einem TLS-terminierenden Proxy betreiben (https/wss); die App übernimmt das Original-Schema aus den Forwarded-Headern. Details: docs/06. Ohne echte Google-Credentials startet die App trotzdem (Dummy-Defaults), aber der Login funktioniert dann nicht.
Empfohlen ist die Installation als Claude-Code-Plugin (analog zum Telegram-Plugin):
kein manuelles .mcp.json, keine node-Befehle. Der manuelle Weg bleibt als Variante B.
1. Installieren (in einer beliebigen claude-Session):
/plugin marketplace add martinvidec/oa-shell
/plugin install oa-shell@oa-shell
Beim Aktivieren fragt Claude Code die App-URL ab (app_url, Default
http://localhost:8080) — auf deine laufende App setzen, z. B. http://localhost:8090.
Aus einem lokalen Checkout statt von GitHub:
/plugin marketplace add /ABS/PFAD/zu/oa-shell.
2. Anmelden (Device Grant) per mitgeliefertem Slash-Command — öffnet den Browser,
speichert das kontogebundene Token unter ~/.oa-shell/credentials.json (Rechte 0600):
/oa-shell:login # nutzt die konfigurierte App-URL
/oa-shell:login http://localhost:8090 # oder URL explizit übergeben
3. Channel an eine Session hängen — im gewünschten Arbeitsverzeichnis starten:
claude --dangerously-load-development-channels plugin:oa-shell@oa-shellBeim ersten Start einmalig den Ordner-Trust-Dialog im Terminal bestätigen.
Der Development-Flag bleibt nötig: eigene Channels sind in der Research-Preview nicht auf Anthropics Allowlist (nur die offiziellen Plugins wie Telegram sind es). Bequem per Alias:
alias claude-oa='claude --dangerously-load-development-channels plugin:oa-shell@oa-shell'. Nach Channel-Code-Änderungen das Bundle neu bauen:cd channel && npm ci && npm run bundle.
cd channel
npm ci
npm run build
npm link # optional: macht `oa-shell` und `oa-shell-channel` global verfügbar
export OASHELL_APP_URL="http://localhost:8080" # bzw. deine Server-URL
oa-shell login # ohne npm link: node dist/login.jsIn dem Verzeichnis, in dem Claude arbeiten soll, eine .mcp.json anlegen …
{
"mcpServers": {
"oashell": {
"command": "oa-shell-channel",
"env": { "OASHELL_APP_URL": "http://localhost:8080" }
}
}
}Ohne
npm link:"command": "node", "args": ["/ABS/PFAD/oa-shell/channel/dist/server.js"].
… und Claude mit angehängtem Channel starten (im selben Verzeichnis):
claude --dangerously-load-development-channels server:oashellIm Browser erscheint deine Session in der Seitenleiste. Auswählen → chatten. Will Claude
ein Tool nutzen (z. B. Write, Bash), erscheint ein Freigabedialog (Erlauben/Ablehnen,
optional mit Begründung). Der Datei-Browser rechts zeigt das Arbeitsverzeichnis (read-only).
Sessions lassen sich umbenennen und trennen (Trennen widerruft das Token). Mehrere Sessions
parallel sind möglich; jede hat ihren eigenen Chat-/Datei-Kontext.
Zum schnellen lokalen Ausprobieren — und als Basis der E2E-Tests — gibt es ein
Dev-Profil e2e, das Google-Login und Device-Grant durch zwei einfache Endpunkte
ersetzt. So lässt sich der echte Pfad (echte claude-Session + echter Channel + App)
ohne Google durchspielen. Nur lokal — siehe Warnung unten.
App mit dem Profil starten:
cd app
JAVA_HOME="$(brew --prefix openjdk@21)" \
java -jar target/oa-shell-app-0.0.1-SNAPSHOT.jar --spring.profiles.active=e2eBrowser ohne Google anmelden: http://localhost:8080/e2e/login?user=demo öffnen
(setzt die Session-Cookie), dann auf / gehen.
Channel ohne Device-Grant verbinden — Token holen und als Credentials ablegen
(gleicher user wie beim Login, damit Browser und Channel demselben Konto gehören):
TOKEN=$(curl -s "http://localhost:8080/e2e/token?user=demo")
printf '{"appUrl":"http://localhost:8080","accessToken":"%s","tokenType":"Bearer","obtainedAt":0}' \
"$TOKEN" > /tmp/oashell-e2e.jsonDann in der .mcp.json (aus Schritt 2) OASHELL_CREDENTIALS auf /tmp/oashell-e2e.json
zeigen lassen und claude --dangerously-load-development-channels server:oashell starten.
Die Session erscheint im Browser; Chat, Freigaben und Datei-Browser funktionieren wie im
echten Betrieb.
⚠️ Nur für lokale Tests. Unter Profile2ekann sich jeder, der die App erreicht, als beliebiger Nutzer anmelden bzw. ein Channel-Token minten (E2eLoginController/E2eTokenController). Das Profil niemals in Produktion aktivieren.
# Web-App: Unit- (Surefire) + E2E-Tests (Playwright/Failsafe)
cd app && JAVA_HOME="$(brew --prefix openjdk@21)" mvn verify
# einzelner Unit-Test: mvn -ntp -Dtest=PermissionServiceTest test
# einzelner E2E-Test: mvn -ntp -Dit.test=ChatFlowIT verify
# E2E sichtbar/headed: mvn -ntp -Dplaywright.headless=false verify
# Channel: TypeScript-Build + Tests
cd channel && npm ci && npm testCI (.github/workflows/ci.yml) führt beides bei jedem Push aus.
| Pfad | Inhalt |
|---|---|
app/ |
Spring-Boot-Web-App (Maven, JDK 21), Thymeleaf + Vanilla JS |
channel/ |
Node/TypeScript-Channel-Paket (oa-shell login + Channel-Server) |
spike/ |
Validierter End-to-End-Durchstich (Referenz/Wegwerf) |
docs/ |
Konzept · Ist-Analyse · Anforderungen · Spezifikation · Spike-Report · Härtung |
- Öffentliches Repo: keine Secrets/Keys/Tokens einchecken (
.gitignoredeckt sie ab). Google-Credentials kommen ausschließlich aus der Umgebung. - Research Preview: Das Channels-Feature von Claude Code ist Vorschau – Flag/Protokoll
können sich ändern. Der Channel-Vertrag ist in
channel/src/protocol.tsgekapselt. - Bekannte Folge-Schritte (siehe docs/06): persistente + verschlüsselte Token-Speicherung (aktuell in-memory), OS-Sandboxing der Channel-Prozesse, echtes Token-Streaming (Antworten kommen derzeit als ganze Nachrichten).