claude-preview-mcp
README.md
# claude-preview-mcp
> Dev-server lifecycle + Chrome browser automation, as an MCP server.
> The `preview_*` tool family that any MCP-compatible agent can use.
This is the [Anthropic Claude_Preview plugin](https://docs.claude.com/en/docs/claude-code/preview) re-implemented as a **standalone MCP server** so it works in **Claude Code**, **OpenCode**, **Kilo Code**, and any other MCP-compatible agent — with one canonical `launch.json` per project.
The repo ships the patched-upstream source (TypeScript) + pre-built JS, so installs don't need a build step. A single `install.sh` handles all three target agents.
---
## What you get
`mcp__Claude_Preview__preview_*` tools (14 total):
| Tool | What it does | Browser? |
|---|---|---|
| `preview_start` | Spawn a dev server from `.claude/launch.json` (PID, ready detection, port inference) | — |
| `preview_stop` | SIGTERM / SIGKILL a running server | — |
| `preview_list` | List configured + currently-running servers | — |
| `preview_logs` | Last N lines of combined stdout+stderr (default 80, max 500) | — |
| `preview_navigate` | Open a URL in a headless Chrome | yes |
| `preview_screenshot` | PNG/JPEG screenshot, base64 inline or saved to disk | yes |
| `preview_snapshot` | A11y tree (tag, role, name, value, focusable) | yes |
| `preview_inspect` | DOM element by CSS selector (rect, attrs, text, display) | yes |
| `preview_click` | Click an element by CSS selector | yes |
| `preview_fill` | Fill an input/textarea/select by CSS selector | yes |
| `preview_eval` | Run JavaScript in the page, return JSON-serializable result | yes |
| `preview_console_logs` | Captured console messages (filter by substring or type) | yes |
| `preview_network` | List captured requests, or fetch one response body by URL | yes |
| `preview_resize` | Resize the preview viewport | yes |
Browser-required tools use the system's Chrome (`/Applications/Google Chrome.app` on macOS, `google-chrome` / `chromium` on Linux).
---
## Install (RECOMMENDED: Claude Code as a plugin)
```bash
# 1. Clone
git clone https://github.com/MichaelTendoSsemwanga/claude-preview-mcp
cd claude-preview-mcp
# 2. Pick your agent
./scripts/install.sh claude-code # one-line: plugin cache, plugin.json, done
./scripts/install.sh opencode # ~/tools + ~/.config/opencode/opencode.jsonc
./scripts/install.sh kilo # ~/tools + ~/.kilocode/mcp_settings.json
./scripts/install.sh all # all three
```
`install.sh`:
- Copies the committed `build/` into the right agent location
- Provisions `node_modules/` (just `@modelcontextprotocol/sdk` + `puppeteer-core`, ~25MB) into the same location via a temp scratch dir — so the repo itself stays light
- Writes / merges the MCP config (uses `jq` for an atomic JSON merge that preserves any existing config)
- Restores `.claude-plugin/plugin.json` for Claude Code
### Per-agent activate
| Agent | Activate |
|---|---|
| **Claude Code** | restart; tools appear as `mcp__Claude_Preview__preview_*` |
| **OpenCode** | `opencode mcp list` → should show `✓ Claude_Preview connected` |
| **Kilo Code** | VS Code: Cmd-Shift-P → "Developer: Reload Window" |
### Why Claude Code is recommended
- One-line setup (drop the plugin into the cache, no MCP config to touch)
- Plugin lifecycle is managed by Claude Code — auto-spawned on launch, auto-killed on exit
- The `plugin.json` uses `${CLAUDE_PLUGIN_ROOT}` so the install path is stable across moves/upgrades
- Same `launch.json` file works in Claude Code's built-in preview (which uses the `configurations` format) AND this MCP
### Why other agents
OpenCode and Kilo don't have a plugin system. They register MCP servers by hand-rolling a config file, and they don't ship a `configurations` preview concept. This MCP bridges that gap: they get the same dev-server workflow, using the same `launch.json`, just with the extra hop of an MCP server entry in their config.
---
## `.claude/launch.json` (the only project-side file)
The MCP server reads dev-server definitions from `<project>/.claude/launch.json`, walking **upward** from the agent's `process.cwd()` until it finds one. Two formats supported, picked in priority order:
### Claude Code format (preferred)
```json
{
"version": "0.0.0",
"configurations": [
{
"name": "back-office",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "dev"],
"port": 5173
},
{
"name": "tkt-backend",
"runtimeExecutable": "bash",
"runtimeArgs": ["-c", "cd /Users/loft/tkt-backend && bun run --hot src/index.ts"],
"port": 3001
}
]
}
```
Fields: `name`, `runtimeExecutable`, `runtimeArgs` (array), `port`, `autoPort` (advisory), `env` (object), `url` (optional — overrides the synthesized `http://localhost:{port}`).
The MCP translates internally: `runtimeExecutable`→`command`, `runtimeArgs`→`args`, `port`→`url`.
### Legacy `servers` format (still supported)
```json
{
"servers": [
{
"name": "web",
"command": "npm",
"args": ["run", "dev"],
"url": "http://localhost:3000",
"readyPattern": "Local:"
}
]
}
```
Use this if you have a project that already used it. Don't mix the two formats in the same file (only `configurations` is read when both are present).
---
## Repo layout
```
claude-preview-mcp/
├── .claude-plugin/
│ └── plugin.json # Claude Code plugin manifest
├── src/ # TypeScript source (patches live here)
│ ├── index.ts # MCP server entry, tool definitions, handlers
│ ├── process-manager.ts # launch.json parser + dev-server spawn
│ ├── browser-manager.ts # headless Chrome session
│ └── schemas.ts # zod input schemas
├── build/ # Pre-compiled JS (committed; don't need to build)
│ └── *.js, *.js.map
├── scripts/
│ └── install.sh # Universal installer (claude-code | opencode | kilo | all)
├── docs/
│ └── ARCHITECTURE.md # Claude Code plugin internals + MCP protocol details
├── package.json # Name, deps, build script
├── package-lock.json # Locked dep versions
├── tsconfig.json # TS compiler config (Node16 ESM, strict)
├── .gitignore
├── LICENSE # MIT
└── README.md # this file
```
---
## Develop / rebuild
```bash
# Edit src/*.ts
npm install # one time, fetches dev deps
npm run build # tsc → build/
git add build/ src/ # commit both
```
The install script will use the freshly-built `build/` on the next run.
---
## Smoke test (any agent)
In the agent's session:
1. `mcp__Claude_Preview__preview_list` → returns `{configured: [...], running: []}`
2. `mcp__Claude_Preview__preview_start {"name": "<one-of-your-configurations>"}` → starts it
3. `mcp__Claude_Preview__preview_list` → now shows it in `running[]`
4. (browser) `preview_navigate {"url": "http://localhost:5173"}` then `preview_snapshot`
5. `preview_logs {"name": "<name>", "tail": 50}` → see the dev-server output
6. `preview_stop {"name": "<name>"}` → SIGTERM
Or from the shell, no agent needed:
```bash
node ./build/index.js &
sleep 1
# Talk JSON-RPC to stdin/stdout (use the test script in /tmp, or any MCP client)
kill %1
```
---
## What's patched vs upstream
The Anthropic Claude_Preview plugin (this repo's source) ships with four bugs that surface under modern MCP clients (Claude Code, OpenCode 1.17+ — both use protocol `2025-11-25`). All four are fixed in `src/`:
| # | Symptom | Fix location |
|---|---|---|
| 4a | `preview_network` schema malformed (`anyOf` at top level) → whole tool list rejected | `src/index.ts` |
| 4b | Only reads legacy `servers` format, ignores Claude Code's `configurations` | `src/process-manager.ts` |
| 4c | `preview_eval "() => ({foo:1})"` returns undefined → result validation fails | `src/index.ts` |
| 4d | `preview_screenshot` returns invalid Base64 (Uint8Array.toString pitfall) | `src/index.ts` |
| 4e | `preview_fill` "Illegal invocation" on non-input elements | `src/index.ts` |
Each patch has an inline comment explaining the original cause. See `docs/ARCHITECTURE.md` for the long version with the diagnostic traces.
---
## Caveats
- **One Chrome per MCP server.** If you have this AND `chrome-devtools-mcp` registered, each spawns its own Chrome instance. Don't run both pointing at the same page.
- **One dev server per port.** Two preview systems (Claude Code's built-in + this MCP) can't both bind the same port. Use one per project.
- **Process state isn't shared.** Servers started via this MCP die when the MCP process exits. Claude Code's built-in preview is more durable.
- **`launch.json` walks up.** If you run an agent from `~/` expecting project launch files, it won't — `cd` into the project root first.
- **Per-agent permissions.** Most agents prompt on first tool use. Allowlist `Claude_Preview` (or each individual tool) to stop the prompts. The Kilo Code install includes an `alwaysAllow` block by default.
- **macOS Chrome path is hard-coded** in `browser-manager.ts` (looks for `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome` first, then `google-chrome` on PATH). Override with `CHROME_PATH` env var.
---
## License
MIT — see [LICENSE](LICENSE). Bundled dependencies (`@modelcontextprotocol/sdk`, `puppeteer-core`) retain their own licenses.
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues