Skip to main content
Glama
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.