A web UI for Claude Code that replaces default terminal prompts with a browser-based interface. Approve/deny tool calls, submit prompts, upload images, and manage sessions from your phone, tablet, or any browser on your network.
Permission approval flow:
Claude Code Web browser
| |
|-- PermissionRequest hook ------> |
| (permission-request.sh) |
| writes .request.json to |
| /tmp/claude-webui/ |
| |
| server.py |
| polls queue dir |
| serves web UI --------> | User sees request
| | clicks Allow / Deny
| writes .response.json <--|
| |
|<-- hook reads response |
| (allow or deny) |
Prompt submission flow (non-tmux mode):
Claude Code Web browser
| |
|-- Stop hook fires |
| (stop.sh) |
| writes .prompt-waiting.json |
| |
| server.py |
| shows prompt input ---> | User types new prompt
| | clicks Submit
| writes .prompt-response |
| |
|<-- hook reads response |
| (blocks stop, passes prompt) |
Prompt submission flow (tmux mode):
Claude Code Web browser
| |
|-- Stop hook fires |
| (stop.sh) |
| writes .prompt-waiting.json |
| approves immediately |
| |
| server.py |
| shows prompt input ---> | User types new prompt
| | clicks Submit
| |
|<-- tmux send-keys delivers prompt |
permission-request.sh—PermissionRequesthook. Receives the tool call, writes a.request.jsonto/tmp/claude-webui/, and polls for a.response.json.server.py— Python HTTP server (port 19836). Serves a single-page UI that shows pending requests and lets you approve/deny them.post-tool-use.sh—PostToolUsehook. Cleans up stale request/response files after a tool finishes executing.stop.sh—Stophook. When Claude finishes a task, writes a.prompt-waiting.jsonso the Web UI can accept a follow-up prompt.user-prompt-submit.sh—UserPromptSubmithook. Cleans up waiting files when a prompt is submitted (from terminal or tmux send-keys).install.sh— Registers the hooks in a project's.claude/settings.json(or~/.claude/settings.jsonwith--global).
- Allow / Deny — approve or reject individual tool calls
- Always Allow — approve and add the pattern to
settings.local.jsonso it won't ask again - Allow Path — for Write/Edit tools, allow all operations under a directory with hierarchical selection
- Split Always Allow — compound Bash commands (pipes and
&&) are split into individual patterns - Session-level auto-allow — server-side evaluation with multi-select support
- Prompt submission — submit follow-up prompts from the Web UI when Claude is idle
- tmux mode — in tmux sessions, prompts are delivered via
send-keysfor seamless operation - AskUserQuestion support — answer Claude's questions with option selection or custom text
- Image upload — attach images in the Web UI prompt area
- WebFetch/WebSearch — permission handling for web access tools
- Graceful fallback — when the server is offline, all hooks auto-approve so Claude Code works normally
- Auto-cleanup of stale requests (dead processes)
- Dark-themed, mobile-friendly UI
- Python 3
jqcurl- Bash
uuidgen(available by default on macOS; installuuid-runtimeon Debian/Ubuntu)
-
Clone this repo anywhere you like:
git clone https://github.com/gooooloo/claude-code-webui.git
-
Start the server:
/path/to/claude-code-webui/server.py
-
Install the hooks:
# For a single project (run from project directory): /path/to/claude-code-webui/install.sh # Or install globally (all projects): /path/to/claude-code-webui/install.sh --global
-
Restart Claude Code if it's already running — hooks are loaded at startup and won't take effect until the next session.
-
Open
http://localhost:19836in your browser (or use your machine's LAN IP from a phone/tablet). -
Run Claude Code — permission requests will appear in the web UI instead of the terminal.
Each hook's behavior depends on whether the server is running and whether Claude Code is inside a tmux session:
| Hook | Trigger | Non-tmux + Server Online | Non-tmux + Server Offline | tmux + Server Online | tmux + Server Offline | Timeout |
|---|---|---|---|---|---|---|
PermissionRequest (permission-request.sh) |
Claude requests tool permission | Write request file → poll for approval → allow/deny | Allow immediately, no file written | Same as non-tmux | Allow immediately, no file written | 24h |
PostToolUse (post-tool-use.sh) |
After tool execution | Clean up request/response files | Same (local files only) | Same | Same | 5s |
Stop (stop.sh) |
Claude is about to stop | Write waiting file → poll for new prompt → block/approve | Approve immediately, no file written | Write waiting file → approve immediately (Web UI uses tmux send-keys) | Approve immediately, no file written | 24h |
UserPromptSubmit (user-prompt-submit.sh) |
User submits a prompt | Clean up waiting files for current session | Same (local files only) | Same | Same | 5s |
When the server is offline, all hooks gracefully fall back to non-blocking behavior — permissions are auto-allowed and stop hooks approve immediately — so Claude Code works normally without the web UI.
The server binds to 0.0.0.0:19836 by default, making it accessible from your local network. This is intentional — it allows you to approve requests from a phone or another device. If you only need local access, change the bind address to 127.0.0.1 in server.py.
There is no authentication on the web UI. Anyone on your network who can reach port 19836 can approve or deny requests. Use on trusted networks only, or add your own auth layer.
MIT