Z1 is a small proxy that lets Claude Code control your React App, without code changes. It allows to test error states, loading states, backend down-services, user journey or whatever and Claude can reason having access to both your codebase and what's really happening!
z1.mov
It allows to intercept, delay or drop any request — and control the live page — just by asking:
You: delay GET /api/users by 3 seconds, check if the loading state renders correctly
Claude:
z1_inject({ match: "GET /api/users", action: "delay", params: { ms: 3000 } })
→ triggers page interaction
z1_observe({ path: "/api/users" })
→ sees durationMs: 3042, status: 200
z1_eval({ code: "document.querySelector('.skeleton') !== null" })
→ true — skeleton rendered correctly
z1_clear()
→ rule removed, proxy back to normal
e2e testing with the power of Claude Code reasoning to spot bugs and fix them directly. I chose Claude to experiment, but it can really be accessed by any agent with just a tweak in the code!
Z1 sits between your browser and your dev server. Every HTTP request flows through it. Every React component in the fiber tree is visible. Claude can read and control the live page. All of that context is streamed via MCP tools, in real time.
Claude can:
-
Observe — see every request, response status, timing, and which React component triggered it
-
Intercept — add latency, drop requests, throttle bandwidth, or return mock responses
-
Inspect — read browser console output (opt-in)
-
Understand — see the live React component tree without reading source code
-
Control — evaluate JavaScript in the page, read DOM state, reload the browser
No changes to your app. The proxy injects a small client bundle into every HTML page it serves, which patches fetch, XHR, and optionally console.*, then streams everything back over a WebSocket.
The proxy spawns your dev server as a child process and backgrounds itself, then Claude Code takes over the terminal:
# inside the project
bun run /path/to/z1/cli.ts proxy localhost:<PORT OF DEV SERVER> --spawn "bun dev" # or the same command that spins the dev server# inside the project start claude and it will auto-detect the mcp
claude bun run /path/to/z1/cli.ts proxy localhost:<PORT OF DEV SERVER> --spawn "bun dev" & claudeOpen http://localhost:4000. The floating panel appears automatically.
On first run, .mcp.json is written to your project root with a session token. Claude Code picks it up automatically — no manual MCP setup needed.
To stop everything: exit Claude Code, then kill %1.
| Flag | Default | Description |
|---|---|---|
--port 4000 |
4000 | Port the proxy listens on |
--spawn "cmd" |
— | Start your dev server as a child process, killed on exit |
--console |
off | Enable browser console capture (off by default — see Security) |
--bodies |
off | Include request and response bodies in z1_observe output |
--allow-https |
off | Allow HTTPS targets (e.g. a local HTTPS dev server) |
| Tool | What it does |
|---|---|
z1_observe |
List captured requests. Filter by method, path, limit with n. |
z1_inject |
Add a behavior rule: delay, respond, drop, or throttle. Accepts ttl_ms (default 5 min). |
z1_clear |
Clear active rules. Pass match to remove one rule, omit to clear all. |
z1_rules |
List active rules with source ([mcp]/[browser]) and time until expiry. |
z1_console |
List browser console entries. Filter by level, limit with n. Requires --console. |
z1_components |
List all React components currently in the fiber tree (live). |
z1_query |
Query a DOM element by CSS selector. Returns tag, text, value, visibility, rect, attributes. Set all=true for up to 20 matches. |
z1_storage |
Read localStorage or sessionStorage. Pass key for one value, omit for all entries. |
z1_click |
Click a DOM element by CSS selector. |
z1_fill |
Set an input's value and fire input/change events (works with React controlled inputs). |
z1_navigate |
Navigate to a URL or relative path. |
z1_reload |
Reload the browser page. |
Rules match requests by method and path.
Match format: "METHOD /path", "* /path" (any method), or just "/path".
| Action | Params | Behaviour |
|---|---|---|
delay |
{ ms: number } (max 30 000) |
Waits N ms then forwards normally |
respond |
{ status, body } (body max 512 KB) |
Returns immediately — never contacts the real server |
drop |
— | Returns 503 with X-Z1-Drop: true |
throttle |
{ kbps: number } |
Streams the response at the given bandwidth limit |
Longest match wins. Exact method match beats wildcard *. MCP-injected rules expire after 5 minutes by default — pass ttl_ms: 0 to make a rule permanent.
Browser (localhost:4000)
│ HTTP
▼
src/proxy/server.ts ← intercepts every request, applies rules, injects client bundle
│ forward (if no rule matches)
▼
Dev server (localhost:3001)
src/proxy/server.ts
│ WebSocket /__devproxy/ws (session token required)
▼
src/proxy/control.ts ← WS hub: ring buffers (500 captures, 500 console entries),
│ header redaction, rate limiting, broadcast
│
├── Browser panel ← floating UI injected into every page (Network + Console tabs)
└── src/mcp/server.ts ← stdio JSON-RPC → AI agent (claude-code by default)
Z1 is a local development tool only.
| Surface | Status |
|---|---|
| WS control plane auth | Session token required on every connection — written to .mcp.json at startup, never committed (gitignored) |
| Non-localhost targets | Hard-blocked. No override flag. |
| HTTPS targets | Blocked unless --allow-https is passed |
Sensitive request headers (Authorization, Cookie, etc.) |
Auto-redacted before storage or broadcast |
| Console log PII | Console capture is off by default. Use --console to opt in. When enabled, JWT/Bearer/password patterns are redacted automatically. |
| Request and response bodies | Never read by Claude unless --bodies is passed. Bodies are not captured or stored by default — only metadata (method, path, status, timing, headers). Enable with --bodies only if you need body-level debugging, and be aware that response bodies may contain PII or secrets. |
httpOnly cookies |
Never visible to Claude — or to any JavaScript. httpOnly cookies are inaccessible to document.cookie and to the injected client bundle by browser design. At the proxy level, the full Cookie header is visible in transit but is auto-redacted before storage or broadcast, so its value never reaches Claude's context window. |
localStorage / sessionStorage |
Readable on demand via z1_storage (MCP tool). Values are never passively captured — only read when Claude explicitly calls the tool. All returned values pass through the same JWT/Bearer/password redaction as console logs before reaching Claude's context window. |
| CORS proxy | Loopback-only + session token required — other machines and unauthorized local processes are blocked |
/__devproxy/cors scheme |
Only http/https allowed — file://, ftp:// etc. return 403 |
| Rule injection | Validated before storage: action must be known, delay.ms ≤ 30 000, respond.body ≤ 512 KB, throttle.kbps 1–100 000 |
| Rule TTL | MCP rules expire after 5 min by default — no forgotten mocks |
| WS rate limiting | 100 messages/second per client — excess disconnects the client |
| Response body buffering | Non-HTML responses > 50 MB stream through without buffering |
| CSP | Stripped from HTML responses (necessary for injection) — deliberate trade-off |
| MCP client gate | Rejects unknown clients at initialize handshake — allowlist configurable |
DOM commands (z1_eval, z1_reload) |
Only MCP/server clients can issue DOM commands — browser tabs are blocked at the WS layer |
I hope you enjoy it!