pickfix-mcp
# pickfix-mcp
[](https://www.npmjs.com/package/pickfix-mcp)
[](LICENSE)
The local half of **Pickfix**. The Pickfix browser extension lets developers, QA and PMs pick an element on a running web app, say what is wrong (or rewrite the text in place, comment on the page, record the steps to a bug) and press **Send to Claude**. `pickfix-mcp` receives that feedback on your machine and hands it to the coding agent working on the repository, which fixes it and reports back to the extension.
```text
Chrome: Pickfix extension ──WebSocket, 127.0.0.1──▶ pickfix-mcp ──MCP──▶ Claude Code
pick · comment · send (extension origin only) queue fixes the code
◀──────────────── Queued → Claude is fixing → Done, with a summary ─────────────────
```
[Tiếng Việt](#tiếng-việt) · [Privacy](PRIVACY.md)
## Quick start
1. **Install the extension:** [Pickfix on the Chrome Web Store](https://chromewebstore.google.com/detail/eehanlcaccamfaalnfcikkdneffjkife) (in review; the link works once it is published).
2. **Install the plugin in Claude Code**, in your project:
```text
/plugin marketplace add ledutu-studio/pickfix-mcp
/plugin install pickfix@pickfix
```
Restart Claude Code. The plugin starts one `pickfix-mcp` server per session.
3. **Open the Pickfix panel** on a local page in Chrome. It finds every running session by itself; there is nothing to pair. If it does not, use [Connect manually](#connect-manually-with-a-port).
Then pick an element, write what should change and press **Send to Claude**. Requires Node.js 20 or newer.
### Let Claude start fixing as soon as feedback arrives (optional)
Channels are a Claude Code research preview. To have a batch pushed straight into the session, start Claude with:
```bash
claude --dangerously-load-development-channels plugin:pickfix@pickfix
```
Claude Code shows a warning first; choose **I am using this for local development**. A shell alias helps: `alias claudefix='claude --dangerously-load-development-channels plugin:pickfix@pickfix'`.
Without the flag everything still works: run `/pickfix:fix` when the extension shows **Queued**. A hook also reminds Claude of waiting feedback when you send a prompt.
## Connect manually with a port
The extension looks for sessions on ports 47400–47409. When a session is not found there (all ten are taken, another program owns the range, or you started the server on a port of your own), connect to it by port:
1. Ask the agent for the port: run the `pickfix_status` tool (in Claude Code, ask *"what is the Pickfix status?"*). It prints `Extension link: listening on ws://127.0.0.1:<port>/pickfix`.
2. In the Pickfix panel choose **Connect manually** (on the *No Claude Code session* card, under the session list, or after **Change**), enter the port and press **Connect**. Pasting the whole `ws://127.0.0.1:<port>/pickfix` address works too.
The session becomes the one this site sends to, and the extension remembers the port (the last five) and looks there again on every scan, so it reconnects after the session restarts on the same port. If the connection fails, the panel says why: nothing answered on that port, the server refused the handshake, or its version does not match the extension.
### Choose the port: `PICKFIX_PORT`
Set `PICKFIX_PORT` to make the server listen on that port only, from 1024 to 65535, instead of the first free one of 47400–47409. A fixed port is handy when the default range is blocked or when you want the extension to reach a session at a port you know in advance.
```bash
PICKFIX_PORT=51234 claude
```
For clients with an `mcpServers` JSON file, put it in `env`:
```json
{
"mcpServers": {
"pickfix": { "command": "npx", "args": ["-y", "pickfix-mcp"], "env": { "PICKFIX_PORT": "51234" } }
}
}
```
With `PICKFIX_PORT` the server does not fall back to another port: if the port is in use, or the value is not a valid port, `pickfix_status` says so and the extension link stays off until you restart the session with a free port. Give each session its own port; two sessions cannot share one. A port outside 47400–47409 is only found through **Connect manually** (once entered, the extension keeps scanning it).
## Other agents (Cursor, Codex, Claude Desktop, …)
Run the server with `npx -y pickfix-mcp` as a stdio MCP server. For clients that use an `mcpServers` JSON file (Cursor's `.cursor/mcp.json`, Claude Desktop):
```json
{
"mcpServers": {
"pickfix": { "command": "npx", "args": ["-y", "pickfix-mcp"] }
}
}
```
Codex (`~/.codex/config.toml`):
```toml
[mcp_servers.pickfix]
command = "npx"
args = ["-y", "pickfix-mcp"]
```
Start the client from your project folder: the server queues feedback per repository. These clients have no channel push: ask the agent to use the `fix` prompt, or to call `pickfix_list_batches` and follow the tool descriptions.
## What the agent gets
| Tool | Purpose |
|---|---|
| `pickfix_status` | Session, repository, port (for Connect manually), batch counts |
| `pickfix_list_batches` | Queued and working batches (or by status) |
| `pickfix_claim_batch` | Claims a batch and returns its items as markdown plus screenshots and reference images |
| `pickfix_report` | Reports `done` / `partial` / `failed` with a summary and per-item results |
| `pickfix_import` | Queues a JSON file exported from the extension |
A batch can be claimed by one session only, so two Claude windows on the same repository never fix the same feedback twice.
## Security model
- The server listens on `127.0.0.1` only, on the first free port of 47400–47409 (or on `PICKFIX_PORT` when set), path `/pickfix`. Connecting manually to a port changes nothing below: the same checks apply on every port.
- Plain HTTP requests get an empty `404` with no CORS headers. The extension sends `HEAD /` to each port it scans and opens a WebSocket only where something answers: Chrome slows every new WebSocket down for seconds once a few dozen have failed, so the scan must not fail with sockets. Keep this answer if you change the server.
- A connection must come from the Pickfix extension (`Origin: chrome-extension://<Pickfix id>`) to a loopback `Host`; web pages, other extensions and DNS-rebinding hosts are refused at the handshake. Browsers do not let a page set `Origin`, so this is the gate; there is no pairing step. Programs already running under your account can still connect, as they can to any local port.
- Everything captured from a web page is passed to the agent as fenced, untrusted data with an instruction never to follow it.
- The server has no tool that runs commands or writes files in your repository; code changes go through your agent's normal permissions.
Full privacy policy (English and Vietnamese): [PRIVACY.md](PRIVACY.md).
## Files
```text
~/.pickfix/ 0700
queue/<repo-key>/<batch>/ batch.json, state.json, screenshots, reference images
```
Finished batches are deleted after 7 days. Set `PICKFIX_HOME` to use another directory.
## Protocol
The extension and server speak protocol 3 over WebSocket; the batch format is `pickfix.batch/2`. The original contract is section 5 of `docs/specs/2026-10-02-pickfix-mcp-design.md`; protocol 3's additions (regions, reference images, per-item viewport) are in the extension repo's `docs/specs/2026-10-06-capture-upgrades-design.md`. Types and schemas ship as `@pickfix/protocol` (`packages/protocol`).
| Direction | Messages |
|---|---|
| Server → extension | `server.info`, `welcome`, `batch.accepted`, `batch.status`, `pong`, `error` |
| Extension → server | `hello`, `batch.submit`, `batch.watch`, `batch.cancel`, `ping` |
## Development
```bash
pnpm install
pnpm test # unit tests
pnpm test:e2e # builds, then drives plugin/dist/server.mjs end to end
pnpm build # rebuild plugin/dist (commit the result; a test checks it is fresh)
pnpm compile # type-check
pnpm --filter @pickfix/protocol build # build the protocol package the extension links to
```
`PICKFIX_EXTENSION_IDS=<id>[,<id>]` allows extra extension ids, for unpacked builds made without the Pickfix key.
| Environment variable | Effect |
|---|---|
| `PICKFIX_PORT` | Listen on this port only (1024–65535) instead of the first free one of 47400–47409; see [Connect manually](#connect-manually-with-a-port) |
| `PICKFIX_HOME` | Where the queue lives (default `~/.pickfix`) |
| `PICKFIX_EXTENSION_IDS` | Extra extension ids allowed to connect, comma-separated |
The extension's id is its Chrome Web Store item id, `eehanlcaccamfaalnfcikkdneffjkife`. `EXTENSION_PUBLIC_KEY` in `@pickfix/protocol` is that item's public key (Developer Dashboard → Package → View public key); Google holds the private key, so nothing secret lives on a developer machine.
## Release
From your machine, once `main` is pushed and CI passed:
```bash
pnpm release --dry-run # checks, starts nothing
pnpm release minor # patch (default) | minor | major; asks, then starts publish.yml and follows it
```
`pnpm release` refuses a `main` whose CI did not pass (it skips `[skip ci]` release commits when looking), local commits that are not pushed, a release already running, a `main` with nothing new since the last tag, and a version npm already has. It never changes the version itself; the workflow below does. To ship this and the extension together, in the right order (this server first), run `pnpm release` in the **pickfix** repository (`ledutu-studio/pickfix`, the folder that holds both checkouts); its `docs/release.md` has the whole flow.
- `.github/workflows/ci.yml` runs on every push to `main` and every pull request: type-check, unit tests (which also check that `plugin/dist` is fresh) and the end-to-end tests.
- `.github/workflows/publish.yml` runs only when started by hand (`pnpm release`, or Actions → publish → Run workflow) from `main`. It bumps the version (`patch` by default, or `minor`/`major`) in `package.json`, `plugin/.claude-plugin/plugin.json` and `src/version.ts`, runs the checks, rebuilds `plugin/dist`, publishes to npm, then commits `chore(release): <version>` and tags `v<version>`. Do not change the version by hand.
- npm accepts the upload through [trusted publishing](https://docs.npmjs.com/trusted-publishers), so no npm token is stored. One-time setup on npmjs.com: `pickfix-mcp` → Settings → Trusted publisher → GitHub Actions, repository `ledutu-studio/pickfix-mcp`, workflow `publish.yml`.
- Optional secret `DISCORD_WEBHOOK_URL` posts the result to Discord.
- Claude Code plugin users do not wait for npm: the marketplace reads `plugin/` from `main`.
## Tiếng Việt
Pickfix giúp dev frontend, QA và PM chỉ vào chỗ sai trên giao diện đang chạy, ghi cần sửa gì, rồi gửi thẳng cho Claude Code sửa trong source. `pickfix-mcp` là phần chạy trên máy bạn: nhận feedback từ extension qua `127.0.0.1` và chuyển cho Claude.
1. **Cài extension:** [Pickfix trên Chrome Web Store](https://chromewebstore.google.com/detail/eehanlcaccamfaalnfcikkdneffjkife) (đang chờ duyệt).
2. **Cài plugin trong Claude Code**, ngay trong project của bạn:
```text
/plugin marketplace add ledutu-studio/pickfix-mcp
/plugin install pickfix@pickfix
```
Khởi động lại Claude Code.
3. **Mở panel Pickfix** trên trang localhost. Panel tự tìm các session đang chạy, không cần ghép nối.
**Kết nối thủ công bằng port:** nếu panel không tự tìm thấy phiên (extension chỉ dò các port 47400–47409), chạy tool `pickfix_status` trong Claude Code để xem port (dòng `ws://127.0.0.1:<port>/pickfix`), rồi trong panel chọn **Kết nối thủ công**, nhập port và bấm **Kết nối**. Extension nhớ port này (tối đa 5 port gần nhất) và tự kết nối lại khi phiên khởi động lại. Muốn server chạy trên một port cố định, đặt biến môi trường `PICKFIX_PORT` (1024–65535), ví dụ `PICKFIX_PORT=51234 claude`; với file cấu hình `mcpServers` thì thêm `"env": { "PICKFIX_PORT": "51234" }`. Nếu port đó đang bị dùng, server không tự chọn port khác: `pickfix_status` sẽ báo lỗi để bạn đổi port.
Muốn Claude tự sửa ngay khi nhận feedback, mở Claude bằng `claude --dangerously-load-development-channels plugin:pickfix@pickfix`. Không dùng cờ này thì gõ `/pickfix:fix` khi panel hiện **Đang chờ**. Giao diện extension có tiếng Việt và tiếng Anh, đổi trong phần cài đặt của extension.
**Phát hành phiên bản mới:** sau khi push lên `main` và CI xanh, chạy `pnpm release minor` (hoặc `patch`, `major`; thêm `--dry-run` để chỉ kiểm tra). Script kiểm tra CI, commit chưa push, rồi chạy workflow `publish.yml` (bump version, đẩy lên npm, tag) và theo dõi tới khi xong. Không sửa version bằng tay. Muốn phát hành cả extension, chạy `pnpm release` trong repo **pickfix** (thư mục chứa cả hai repo; MCP trước, extension sau).
Cursor, Codex và các agent khác: thêm MCP server chạy `npx -y pickfix-mcp` (xem cấu hình ở trên). Chính sách quyền riêng tư: [PRIVACY.md](PRIVACY.md).
## License
MIT. See [LICENSE](LICENSE).
TDQS
Scored across 5 tools
Each tool maps to a distinct step in the feedback-batch lifecycle: status (session overview), list_batches (enumerate), claim_batch (acquire), report (close out), and import (ingest external). Status and list_batches both surface batch info, but one is session/connection health while the other enumerates batches, so boundaries stay clear.
All five tools use the same pickfix_ prefix followed by a clear verb or verb_noun (status, list_batches, claim_batch, report, import). The pattern is predictable and readable throughout.
Five tools is well-scoped for a focused claim/work/report workflow, with each tool earning its place. No redundancy or filler.
The surface covers the core lifecycle: inspect session, list batches, claim, receive items, report outcome, plus importing external batches. A release/unclaim path (returning a claimed batch to the queue) is missing, which could be a dead end if a claim is abandoned, but agents can otherwise work around it.