Metadata-Version: 2.4
Name: batch-review-mcp
Version: 0.4.6
Summary: Batch review tool for markdown files and code changes — MCP server + web UI
Project-URL: Homepage, https://github.com/hevangel/batch-review-mcp
Project-URL: Repository, https://github.com/hevangel/batch-review-mcp
Project-URL: Issues, https://github.com/hevangel/batch-review-mcp/issues
License-File: LICENSE
Requires-Python: >=3.13
Requires-Dist: aiofiles>=23.2.1
Requires-Dist: fastapi>=0.115.0
Requires-Dist: fastmcp>=3.0.0
Requires-Dist: gitpython>=3.1.0
Requires-Dist: python-dotenv>=1.2.2
Requires-Dist: python-multipart>=0.0.9
Requires-Dist: uvicorn[standard]>=0.30.0
Requires-Dist: websockets>=12.0
Description-Content-Type: text/markdown

# Batch Review

A collaborative code and markdown review tool that bridges human reviewers and AI agents. Both can browse files, inspect git diffs, leave structured comments, and save a final review report — all from the same UI, in real time.

![Batch Review UI — file tree on the left, rendered Markdown in the centre, and AI-generated review comments on the right](https://raw.githubusercontent.com/hevangel/batch-review-mcp/main/docs/screenshot.png)

---

## Features

| Feature | Description |
|---|---|
| **3-panel review UI** | File explorer + git changes on the left, viewer in the center, comment thread on the right |
| **Markdown rendering** | `.md` files are fully rendered, including GitHub-style math rendered with KaTeX (inline dollar-backtick ``$`...`$``, `$$...$$`, and fenced `math` blocks; plain `$...$` is left as text to avoid currency and stock-symbol false positives) plus Mermaid fenced diagrams with a toolbar toggle between rendered and source views; highlight any paragraph to add a comment |
| **PDF viewing** | `.pdf` files render in the center panel with page-aware text or region comments and reload support |
| **Syntax highlighting** | All common languages via Monaco Editor (Python, TypeScript, Go, Rust, etc.) |
| **Inline git diff** | Click any changed file to view an inline red/green unified diff |
| **Structured comments** | Each comment captures `@filename:L10-15` line references automatically |
| **Outdated recovery** | When a text comment goes stale, refresh its stored highlight from the current file directly from the right panel |
| **AI collaboration** | MCP server exposes many tools so AI agents can review alongside humans — UI updates live, with on-screen notices for agent-driven comment changes |
| **Dual output** | Saves review as both **JSON** (machine-readable) and **Markdown** (human-readable) |
| **Resume session** | On startup, if `{output-dir}/{output}.json` already exists, comments are loaded automatically |
| **Cross-platform** | Runs on Windows and Linux |

### GitHub-style math example

Batch Review renders the same GitHub-style math forms it documents above, including inline
dollar-backtick ``$`...`$`` and block `$$...$$` equations. Plain `$...$` remains text so
sentences about prices or stock symbols do not accidentally become math. For example:

Inline: $`e^{i\pi} + 1 = 0`$

$$
\nabla_\theta J(\theta) = \mathbb{E}_{(s, a) \sim \pi_\theta}
\left[\nabla_\theta \log \pi_\theta(a \mid s)\, R(s, a)\right]
$$

---

## Architecture

At runtime, Batch Review is a single Python application that serves the browser UI, exposes REST and WebSocket endpoints for that UI, and optionally exposes the same review session to MCP clients.

```mermaid
flowchart LR
    CLI["CLI entry point<br/>`uv run batch-review`"]
    Browser["Browser UI<br/>React + Zustand"]
    McpHost["MCP host<br/>Cursor / Claude Desktop / VS Code / other clients"]

    subgraph Backend["Batch Review backend process"]
        Server["FastAPI app<br/>static frontend `/`<br/>REST `/api/*`<br/>WebSocket `/ws`"]
        Mcp["FastMCP server<br/>stdio transport<br/>HTTP transport `/mcp`"]
        State["Shared AppState<br/>repo root, comments, MCP session,<br/>connected WebSocket clients"]
    end

    Repo["Git repository under review"]
    Output["Saved review output<br/>`review_comments.json`<br/>`review_comments.md`"]

    CLI --> Server
    CLI --> Mcp
    Server --> Browser
    Browser -->|"REST reads/writes"| Server
    Browser <-->|"live sync"| Server
    Server --> State
    Mcp --> State
    McpHost -->|"stdio or HTTP MCP"| Mcp
    State -->|"read files, diffs, existing review data"| Repo
    State -->|"save/load review reports"| Output
    State -.->|"broadcast UI events"| Browser
```

- The browser loads the React app from the FastAPI server, fetches files/comments/config over REST, and stays synchronized through `/ws`.
- MCP tools and REST routes both operate on the same in-memory `AppState`, so agent-created comments appear in the UI immediately.
- In `--mcp` mode, the HTTP server keeps the web UI available while FastMCP also runs over stdio for editor and CLI hosts.

---

## Distribution

- **[PyPI — `batch-review-mcp`](https://pypi.org/project/batch-review-mcp/)** — published wheels and sdist for `pip` / `uv`.
- **[Official MCP Registry — `io.github.hevangel/batch-review-mcp`](https://registry.modelcontextprotocol.io/v0.1/servers/io.github.hevangel%2Fbatch-review-mcp/versions/latest)** — latest published server metadata (JSON API; the [registry site](https://registry.modelcontextprotocol.io/) is still preview).

---

## Requirements

- Python ≥ 3.13 with [uv](https://docs.astral.sh/uv/)
- Node.js ≥ 22 (for the one-time frontend build; matches GitHub Actions)

---

## Installation

```bash
git clone https://github.com/hevangel/batch-review-mcp.git
cd batch-review-mcp
uv sync
```

The first run will automatically build the React frontend if `frontend/dist/` does not exist (requires Node.js + npm on `PATH`).

---

## Usage

### Standalone mode (browser opens by default)

```bash
# Review the current directory
uv run batch-review

# Review a specific git repository
uv run batch-review --root /path/to/your/repo

# Custom host / port
uv run batch-review --root /path/to/repo --host 0.0.0.0 --port 9100

# Specify output filenames
uv run batch-review --root /path/to/repo --output review --output-dir /tmp/reviews
# → saves /tmp/reviews/review.json and /tmp/reviews/review.md
```

The server auto-selects a free port in the 9000–9999 range if the default port is already in use.

### MCP stdio mode (Claude Desktop / Cursor / any MCP client; browser opens by default)

```bash
uv run batch-review --mcp --root /path/to/repo
```

The HTTP server starts in a background thread (so the browser UI remains accessible), your default browser opens to the app URL (same as standalone mode; use `--no-browser` to skip), and the MCP stdio transport runs in the main thread.

**Claude Desktop config** (`claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "batch-review": {
      "command": "uv",
      "args": ["run", "batch-review", "--mcp", "--root", "/path/to/repo"]
    }
  }
}
```

### Official MCP registry

This project includes a root-level [`server.json`](./server.json) for the [Model Context Protocol registry](https://registry.modelcontextprotocol.io/) (preview). The entry uses **`registryType": "mcpb"`**: the download URL must be a **public** GitHub release asset, and **`fileSha256`** must match those bytes **exactly** (the [Release](.github/workflows/release.yml) workflow builds the `.mcpb` on **Linux**, which can differ from a pack produced on Windows).

**Recommended order for a new version (e.g. `v0.4.3`):**

1. Bump **`version`** in `pyproject.toml`, **`mcpb/manifest.json`**, and **`server.json`** (top-level `version` plus `packages[0].identifier` URL: `.../releases/download/v0.4.3/batch-review-mcp-0.4.3.mcpb`).
2. Commit and push that release-prep change to `main`.
3. Push the **tag** (e.g. `git tag v0.4.3 && git push origin v0.4.3`).
4. The **Release** workflow builds the Linux artifacts again, runs `scripts/update_server_json_mcpb_sha.py` to patch `server.json` in the workflow workspace from the Linux-built `.mcpb`, runs `scripts/verify_release_mcp_registry.py`, creates or updates the GitHub release, publishes `server.json` to the MCP Registry via **GitHub Actions OIDC** (`mcp-publisher login github-oidc`), and publishes the wheel/sdist to PyPI when **`PYPI_API_TOKEN`** is configured. If `identifier` or versions do not match the tag, or if the patched `server.json` still does not match the Linux-built `.mcpb`, the job **fails** so you never publish a broken asset to the registry.

The separate **[MCP registry preflight (Linux MCPB hash)](.github/workflows/mcp-registry-preflight.yml)** workflow is now optional. It is still useful when you want to inspect the Linux-built bundle bytes before tagging or debug a registry mismatch, but normal releases no longer require copying a SHA into `server.json` by hand.

If a tag already exists but the Release workflow itself needed a workflow-only fix, you can
rerun it manually from **Actions** via `workflow_dispatch` by providing `release_tag`
(for example `v0.4.3`). That manual path checks out the tag you name, overlays the current
release metadata from `main`, and reuses the existing tag name, so you can recover the
release without moving the tag or rebuilding from a different code revision. That rerun path
also repairs MCP Registry publication because the workflow republishes `server.json` from the
current `main` branch metadata and recomputes the Linux `fileSha256` before verify/publish.

`scripts/build_mcpb.py` rewrites the packed archive deterministically after `mcpb pack`, so
the Linux SHA from preflight should match the Linux SHA seen again in the Release workflow
for the same source tree.

Local `mcp-publisher login github` remains useful for one-off manual publishes, but normal
tagged releases no longer require workstation device-code confirmation.

### CLI flags

| Flag | Default | Description |
|---|---|---|
| `--root PATH` | current directory | Git repository to review |
| `--host HOST` | `127.0.0.1` | Bind address |
| `--port PORT` | `9000` | Preferred port (auto-increments if busy) |
| `--output NAME` | `review_comments` | Base filename for saved output (no extension) |
| `--output-dir DIR` | repo root | Directory to write output files |
| `--mcp` | off | Enable MCP stdio transport |
| `--no-browser` | false (omit) | **Normal behavior** (flag omitted): browser opens automatically ~1.2s after the server is ready in standalone, `--dev`, and `--mcp`. Pass `--no-browser` to disable. |
| `--skip-build` | off | Skip the npm build step |

---

## UI Walkthrough

### Left panel — two tabs

**Files tab** — recursive directory tree. Click a file to open it in the center panel.

**Git tab** — lists files changed relative to HEAD with status badges:
- 🟡 `M` modified
- 🟢 `A` added
- 🔴 `D` deleted
- ⚪ `U` untracked

Click a changed file to open it in inline diff mode.

### Center panel

- **No file selected** — shows a lightweight getting-started panel with the app title and a short reminder: open a file or diff on the left, add comments, then save the review.
- **Markdown files** — fully rendered. Relative image embeds render inline, GitHub-style math syntax renders inline and block equations with KaTeX, Mermaid fenced blocks can switch between rendered diagrams and raw source via the center-panel toolbar, links to other repo files open in the app, and links like `other.md#heading` open that file and jump to the heading in the center panel.
- **PDF files** — rendered page-by-page in the center panel. Text-selection comments remember the highlighted text on that page even if you click the toolbar Add button, while region comments keep a page rectangle (`Ctrl+Alt+C`).
- **Code files** — Monaco Editor with syntax highlighting. Select lines and click **+ Add Comment** in the toolbar.
- **Diff view** — Monaco DiffEditor showing original (HEAD) vs working tree inline (red = removed, green = added). Switch back to normal view via the Git tab or by clicking the file in the Files tab.

### Right panel

Each comment shows:
- `@filename:L10-15` reference — click to jump to that location in the center panel
- A text area for your review notes (auto-saves on blur)
- For outdated text comments, a refresh icon inside the textbox that re-captures the current highlighted text at that line range and clears the outdated state
- A delete button

The **💾 Save Review** button saves all comments to:
- `{output-dir}/{output}.json` — machine-readable JSON array
- `{output-dir}/{output}.md` — human-readable Markdown report grouped by file

When the server starts, if that JSON file already exists it is **loaded into the session** so you can continue a saved review (invalid files are skipped with a log warning). Loading a saved review from the right-panel folder button also updates the active review stem shown in the footer so subsequent saves target that loaded review name by default.

---

## MCP Tools

AI agents connect via `http://localhost:PORT/mcp` (HTTP transport) or stdio (`--mcp` flag).

The MCP surface is intentionally **review-first**. Batch Review is not trying to be a
general repo browser or editor API; it is a shared review-state server for:

- identifying the review scope
- inspecting diffs and just enough file context
- adding or updating anchored review comments
- saving or resuming the review session

A typical agent flow is:

1. Call `get_git_changes()`
2. Call `get_git_diff(path)` for files worth reviewing
3. Use `get_file_content(path)` only when extra non-diff context is needed
4. Add or update comments, optionally driving the shared UI with open/highlight/jump tools
5. Save or load the review session with the review file tools

### Repo-local MCP host configuration

This repository includes checked-in defaults so common agents can use **Batch Review** and **Playwright** together:

| Product | Config file | Format |
| --- | --- | --- |
| **Cursor** (editor and `agent` CLI) | [`.cursor/mcp.json`](.cursor/mcp.json) | `mcpServers` with `"type": "stdio"` |
| **VS Code / GitHub Copilot** | [`.vscode/mcp.json`](.vscode/mcp.json) | `servers` with `"type": "stdio"` ([reference](https://code.visualstudio.com/docs/copilot/reference/mcp-configuration)) |
| **Claude Code** | [`.mcp.json`](.mcp.json) | Project `mcpServers` (stdio) |
| **OpenAI Codex CLI** | [`.codex/config.toml`](.codex/config.toml) | `[mcp_servers.NAME]` stdio blocks (loaded for trusted projects) |
| **Gemini CLI** | [`.gemini/settings.json`](.gemini/settings.json) | Top-level `mcpServers` ([guide](https://google-gemini.github.io/gemini-cli/docs/tools/mcp-server.html)) |

All definitions run `uv run batch-review --mcp --root . --skip-build` so the review root is the **workspace directory** hosts use as the server cwd. Cursor’s `agent` CLI does not always expand `${workspaceFolder}` inside `args`, so these configs use `"."` for `--root` (VS Code may still use `${workspaceFolder}` in `.vscode/mcp.json`, which that host expands).

### Verifying stdio MCP

```bash
# Smoke test with the official Python MCP client
uv run python scripts/test_mcp_client.py

# Cursor Agent CLI (after install: https://cursor.com/docs/cli )
cd /path/to/batch_review_mcp
agent mcp enable batch-review
agent mcp list-tools batch-review
agent --approve-mcps -p "Your prompt that may call MCP tools"
```

| Tool | Description |
|---|---|
| `init_batch_review_session(coding_agent, model_name?, client_version?)` | **Call first** once per MCP connection; registers the host and model. Other tools return an error until this succeeds (except `get_config`, `get_review_web_url`, and resource `batch-review://server/urls`). |
| `get_git_changes()` | List changed files vs HEAD |
| `get_git_diff(path)` | Unified diff + original/modified content |
| `add_comment(...)` | Add a review comment; shows a short notice in the UI |
| `update_comment(comment_id, text)` | Edit comment body; UI notice |
| `delete_comment(id)` | Delete a comment; UI notice |
| `clear_all_comments()` | Remove every in-memory comment at once; UI notice |
| `delete_outdated_comments()` | Remove comments with ``outdated`` true; UI notice |
| `list_comments()` | List all in-memory comments |
| `recompute_comment_stale()` | Re-scan files vs ``highlighted_text`` and set each comment's ``outdated`` flag; UI notice |
| `list_review_files()` | List stems of `*.json` reviews in `output_dir` |
| `load_review_by_stem(stem)` | Replace comments from `{stem}.json`; UI notice |
| `save_comments(output_stem?, output_dir?)` | Save JSON + Markdown report, returns paths |
| `get_file_content(path)` | Read file content as structured data (`content`, `line_count`, `language`, `path`) when diff context alone is not enough |
| `list_directory(path)` | Minimal repo navigation helper for hosts that want the review file tree |
| `open_file_in_ui(path, mode)` | Open a file in the center panel (`view` or `diff`); same path+mode refreshes the view |
| `highlight_in_ui(path, line_start?, line_end?, pdf_page?, region_x1?, …)` | **Source files:** ``line_start``–``line_end`` (1-based). **PDFs:** ``pdf_page`` + ``region_*`` normalized 0–1 on that page. **Images:** ``region_*`` in original pixels (omit ``pdf_page``) |
| `jump_to_comment_in_ui(comment_id)` | Same as clicking a comment’s `@file:L…` link: open the file and highlight that anchor |
| `get_config()` | Return `output_stem`, `output_dir`, and `web_ui_url` (when the server has bound) |
| `get_review_web_url()` | Return `web_ui`, `websocket`, and `mcp_http` URLs for the running app |
| *(resource)* | MCP resource URI **`batch-review://server/urls`** — same URL JSON as `get_review_web_url` (`resources/read`); readable **before** `init_batch_review_session` |

Agents should call **`init_batch_review_session`** immediately after connecting (with a non-empty `coding_agent`, e.g. `Cursor` or `Claude Desktop`). The server enforces this: calling any other tool first yields a clear “call init…” error.

Comment **add**, **update**, **delete**, **clear_all_comments**, **delete_outdated_comments**, **recompute_comment_stale**, and **load_review_by_stem** also push a dismissible toast at the bottom of the right panel (similar styling to the post-save path hints).

---

## Output formats

### JSON (`review_comments.json`)

```json
[
  {
    "id": "4c66b5fc-...",
    "file_path": "src/auth.py",
    "line_start": 42,
    "line_end": 55,
    "reference": "@src/auth.py:L42-55",
    "text": "Token is never validated — add expiry check.",
    "highlighted_text": "…",
    "outdated": false,
    "created_at": "2026-04-15T10:00:00+00:00"
  }
]
```

### Markdown (`review_comments.md`)

```markdown
# Code Review

_Generated: 2026-04-15 10:00 UTC — 3 comment(s)_

---

## src/auth.py

### `@src/auth.py:L42-55`
> Token is never validated — add expiry check.

---
```

---

## Development

```bash
# Backend only (no frontend build needed)
uv run batch-review --root . --skip-build --no-browser

# Frontend dev server (proxies API to port 9000)
cd frontend && npm run dev

# Full production build
cd frontend && npm run build
```

For contributor documentation, name `session_history/` files with the actual date of the AI session that produced the change rather than copying the date from an older entry.

---

## License

[MIT](LICENSE)
