claude-react-web
v0.5.10
Published
Local interactive chat platform powered by @anthropic-ai/claude-agent-sdk
Maintainers
Readme
claude-react-web
A local browser UI for @anthropic-ai/claude-agent-sdk.
Features
- Multi-session chat — Up to 3 conversations side-by-side in a 3-column grid
- Streaming responses — Real-time SSE streaming with fine-grained status (thinking / writing / tool use)
- Permission management — Visual tool-usage approval with per-session and global persistence
- Git integration — Status, diff, branches, stashes, and AI-generated commit messages
- Image paste — Drag & drop or paste images directly into the chat
- Keyboard shortcuts —
Cmd+Kcommand palette,Shift+Tabpermission cycling, global shortcuts - Flexible config — Model, permission mode, MCP servers, and more via in-app settings or
config.json - LAN access — Scan a QR code to use from your phone on the same network
Each chat session holds its own stateful Query (the SDK's streaming async generator), so multi-turn conversations, mid-run interruption, model switching, and permission-mode changes all drive a live subprocess.
Quick start
Install globally from npm and run:
npm i -g claude-react-web
claude-react-webOr run it without installing via npx:
npx claude-react-webEither way launches the server on http://127.0.0.1:3456 and opens your browser.
git clone https://github.com/LoopGe/claude-react-web.git
cd claude-react-web
npm install
npm run build
npm run startOn first run a starter ~/.claude-react-web/config.json is scaffolded. Set your Anthropic credentials there before sending messages — the server forwards them to the Claude SDK subprocess:
{
"authToken": "sk-ant-...",
"baseUrl": "https://api.anthropic.com"
}authToken is sent as a Bearer token, so it works against both the official API and Anthropic-compatible proxies (point baseUrl at the relay). You can also fill these in from the in-app settings panel. See CONFIG.md for every field.
CLI options
claude-react-web [options]
-p, --port <port> Server port (default: 3456)
--host <host> Bind host (default: 127.0.0.1). Use 0.0.0.0 to
allow LAN access — this REQUIRES a web access
token (auto-generated if --token is omitted).
--token <token> Shared web access token required to use the UI.
Auto-generated when the host is non-loopback.
-o, --open Open browser on start (default)
--no-open Do not open a browser window
--cwd <path> Default cwd for new sessions
--model <name> Default model for new sessions
--state-dir <path> Where session metadata + config.json are kept
(default: ~/.claude-react-web)
--claude-binary <path> Path to the claude CLI binary (overrides
CLAUDE_CODE_BINARY / PATH auto-detection)
-h, --help Show helpWhen bound to a non-loopback host the server prints a token-bearing URL (plus a scannable QR code) so you can open the already-authenticated UI from a phone on the same network.
Environment variables
Anthropic credentials live in config.json (authToken / baseUrl), not in env vars — the server injects them into each SDK subprocess. The variables below tune the server itself and are all optional:
| Variable | What it does | Default |
| --- | --- | --- |
| CLAUDE_CODE_BINARY | Path to the claude CLI binary (same as --claude-binary) | auto-detected on PATH |
| LOG_LEVEL | Log verbosity (error / warn / info / debug) | info |
| LOG_SCOPES | Comma-separated scope filter for logs | all scopes |
| EVENT_LOOP_PROBE | Set to 0 to disable the event-loop stall probe | enabled |
Any other ANTHROPIC_* variable in the environment is forwarded to the SDK subprocess as-is (except ANTHROPIC_API_KEY, which is intentionally stripped in favour of the authToken Bearer flow).
Configuration file
Most server-side defaults (model list, recap model, commit-message model, upload limits, history cap, max open panels, etc.) are configured via ~/.claude-react-web/config.json. See CONFIG.md for the full field reference, or copy config.example.json to get started:
mkdir -p ~/.claude-react-web
cp config.example.json ~/.claude-react-web/config.jsonDevelop
npm install
npm run dev # tsx watch server (:3456) + vite (:5174, /api proxied)
npm run typecheck
npm run lint
npm testUseful scripts:
| Script | What it does |
| ------------------- | -------------------------------------------------------- |
| npm run dev | Hot-reloading server + Vite dev server side by side |
| npm run build | vite build → dist/client then esbuild → dist/cli.mjs |
| npm run typecheck | tsc --noEmit for both browser and Node tsconfigs |
| npm run lint | ESLint (includes react-hooks) |
| npm run format | Prettier write |
| npm test | Vitest (server unit tests + client hook tests) |
Architecture
graph TB
Browser["Browser UI<br/>React 19 · components · hooks · session-store"]
Browser <-->|"WebSocket (multiplexed) + REST /api"| Routers
subgraph Server["Hono Server · port 3456"]
direction TB
Gate["Auth gate (web token / LAN) · CORS · body-limit"]
Routers["REST routers /api/* + WebSocket hub"]
Gate --> Routers
end
subgraph SM["Session Manager — one live session per tab"]
direction TB
Pool["Session pool"]
Pump["Session Pump → history ring (500) → fan-out"]
Broker["Permission Broker (canUseTool)"]
Health["Health Monitor (stuck-session GC)"]
Pool --> Pump & Broker & Health
end
Reg["Provider Registry<br/>(pluggable AgentProvider)"]
Claude["claude provider → SDK Query"]
SDK["Claude Agent SDK<br/>spawns claude CLI subprocess"]
Anthro["Anthropic Messages API<br/>recap · commit-message (direct)"]
Stores["Disk-backed stores<br/>sessions.json · config.json · MCP · marketplace→plugins · snippets · UI state"]
Git["Git Layer — git.ts owns all execution"]
Disk[("~/.claude/projects/<br/>full transcripts · resumed via options.resume")]
Routers --> Pool
Pool --> Reg --> Claude --> SDK
Broker -.->|canUseTool| Claude
Routers --> Anthro
Routers --> Stores
Pump --> Git
Claude --> Disk
Pump -.->|history-reader| Diskserver/
cli.ts # bin entry — argv, startup banner, QR, browser open
app.ts # Hono app: auth gate, CORS, body-limit, route mounting, static serve
routes/ # REST routers: sessions, permissions, uploads, recap, config,
# health, marketplace (mp), git-write, update, search, skills, hooks
session-manager.ts # multi-session pool, provider wiring, WS fan-out, idle GC
session-pump.ts # drains each provider stream → history ring + subscribers
providers/ # AgentProvider interface + registry; claude provider wraps the SDK Query
permission-broker.ts # parks canUseTool requests until the client decides
session-health.ts # stuck-session detector (mid-turn silence GC)
recap.ts # AI session summaries, via anthropic-api.ts
history-reader.ts # reads ~/.claude/projects transcripts; resume / fork anchors
ws.ts # WebSocket hub (single connection, multiplexed channels)
git.ts # owns ALL git execution (runGit); git-broadcast.ts debounces mutations
git-routes.ts # read-only git endpoints (status, diff, log)
mcp-config.ts # global MCP server store; mcp-routes.ts exposes it
mp-store.ts # homegrown git-repo marketplace → injects Options.plugins
snippet-store.ts # composer text macros (snippet-routes.ts)
ui-state-store.ts # session groups + sidebar order (json-file-store.ts backed)
config.ts # centralised defaults from config.json
persistence.ts # ~/.claude-react-web/sessions.json read/write
auth.ts # web-access token gating (LAN)
exec.ts # child_process helpers; process-monitor.ts watches subprocesses
update-checker.ts # in-app upgrade detection (update-routes.ts)
log.ts # createLogger(scope) — all diagnostics go through this
shared/ # types + logic shared by server and client
# ws-protocol, hooks, skills, mcp-types, permission-request, search/, …
src/
App.tsx # multi-panel chat grid, sidebar, settings overlay, command palette
components/ # Chat, Composer, MessageList, SessionList, GitPanel, CommandPalette,
# MarketplaceTab, McpInstaller, UpdateBanner, SubagentOverlay, …
hooks/ # useWsHub, useChatStream, usePastedImages, usePermissionChannel,
# useGitStatus, useUpdateInfo, useUiState, useSessionRecap, …
session-store/ # client-side message store (reducer + selectors)The server keeps one live provider session per tab — the default claude provider wraps the SDK Query, and session-pump.ts drains it, fanning each SDK message out to every WebSocket subscriber via a single multiplexed connection per tab. Metadata is persisted in ~/.claude-react-web/sessions.json, so sessions survive restarts; the SDK itself stores full conversation history in ~/.claude/projects/ and resumes them via options.resume. Server-side defaults (model list, recap model, commit-message model, upload limits, etc.) are centralised in config.ts and configurable via ~/.claude-react-web/config.json — see CONFIG.md.
Contributing
Issues and pull requests welcome. The codebase has 800+ tests across ~45 files covering the session pool, persistence, git execution, permission broker, WebSocket hub, keyboard shortcuts, and the client hooks — please keep them green (npm test).
