Skip to content

thomscoder/z1

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Z1

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!

What it does

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

    image

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.

How to run

The proxy spawns your dev server as a child process and backgrounds itself, then Claude Code takes over the terminal:

two terminal setup:

# 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 

one terminal setup:

bun run /path/to/z1/cli.ts proxy localhost:<PORT OF DEV SERVER> --spawn "bun dev" & claude

Open 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.

Flags

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)

MCP tools

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 engine

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.

Architecture

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)

Security

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!

About

Let Claude control your React app

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors