reviewzy
by uwuclxdy
README.md
<div align="center">
# reviewzy
**human-in-the-loop mcp review for user-facing text: agents file, a human approves, later sessions apply**
bulk-audit and rewrite user-facing copy across CLIs, TUIs, web UIs, and websites; prose authorship stays human
[](#license)
[](https://modelcontextprotocol.io)
</div>
---
reviewzy is a self-hosted dashboard and mcp server for human-in-the-loop copy review. a coding agent files the exact text it wants to write or rewrite. a human authors or approves each one on the dashboard. a later agent session fetches the approved words and applies them in place. no i18n keys, no content layer: each entry points at the text where it already lives.
file an entry:
```json
{
"project": "my-app",
"entries": [
{
"repo": "https://github.com/me/my-app",
"file": "src/cli/main.ts",
"anchor_text": "error: something went wrong",
"agent_draft": "error: could not reach the reviewzy daemon",
"file_content": "#!/usr/bin/env bun\nconsole.error(\"error: something went wrong\")\n",
"constraints": { "max_len": 48, "tone": "plain, lowercase" }
}
],
"filed_by": "help-text-audit"
}
```
the call answers with the entry as `draft` and a dashboard link for the batch:
```json
{
"batch_id": "01M060XY9GNK6ANVENN4Y4MK7N",
"results": [
{ "id": "01M060XY9GNK6ANVENN4Y4MK7P", "status": "draft", "deduped": false, "updated": false }
],
"dashboard_url": "http://127.0.0.1:3123/?project=my-app"
}
```
## Why
- **file + snippet anchors, no i18n keys:** apply-back is a targeted replace in place
- **humans author, agents apply:** the status machine is server-enforced; only the dashboard reaches `approved` or `rejected`
- **per-project style guide (mcp resource):** merged with the global guide, embedded in `fetch_approved`
- **append-only revision history:** every save is undoable; history survives archiving
- **ntfy and webhook notifications, archive retention:** one ping per filing batch; applied entries archive after `ARCHIVE_AFTER_DAYS`
## How it works
| stage | who | what happens |
|---|---|---|
| file | agent | `file_entries` through the stdio shim, which finds or spawns one shared daemon |
| notify | daemon | pings ntfy or the webhook with a dashboard link |
| author | human | edits, approves, or rejects on the dashboard; saving prose releases it as `approved` |
| fetch | agent | `fetch_approved` (or a blocking `await_approved`) returns text, anchor, constraints, and the merged style guide |
| apply | agent | replaces the anchor, reports through `mark_applied` |
| archive | daemon | applied entries past `ARCHIVE_AFTER_DAYS` move to the archive with their revisions |
## Install
```sh
bunx reviewzy@latest
```
runs the published npm version (the default). to switch to the latest git version:
```sh
bunx github:uwuclxdy/reviewzy
```
pin a commit with `bunx github:uwuclxdy/reviewzy#<sha>` to keep it stable.
requires bun >= 1.3 (the store is `bun:sqlite`; npm/npx not supported). the shim spawns one shared daemon per machine on first use.
from a checkout:
```sh
bun install
bun run daemon
```
### Claude Code plugin
the plugin registers the MCP server and adds a `/reviewzy` slash command plus the reviewzy skill.
```
/plugin marketplace add uwuclxdy/reviewzy
/plugin install reviewzy@reviewzy
```
#### run a checkout as the plugin (dev)
the marketplace above pins the published version. to run your local checkout instead, first repoint the plugin's mcp server at the linked binary: in `.claude-plugin/plugin.json`, change `mcpServers.reviewzy.command` from `bunx` to `reviewzy` and drop `args`. keep that edit uncommitted. the committed value stays `bunx reviewzy@latest` for publish. set `REVIEWZY_DEV=1` in `~/.claude/settings.json`'s `env` (or export it in your shell) so the shim drains and respawns the daemon from the checkout on every session start. then, from the checkout root:
```sh
bun link # reviewzy on PATH, symlinked to bin/reviewzy
claude plugin marketplace add ./ # register the checkout as a marketplace
claude plugin install reviewzy@reviewzy
```
without `REVIEWZY_DEV=1`, the shared daemon is keyed on version: a same-version daemon already running from npm keeps serving, so `src/` edits never reach it. bumping `version` or killing the daemon is the manual fallback.
## Usage
```sh
curl http://127.0.0.1:3123/health
```
```json
{"name":"reviewzy","version":"0.2.0","pid":4172598,"nonce":"881f0e93-8dde-4c75-b559-d2e3d3b223f0","startedAt":"2026-08-16T19:31:28.640Z"}
```
open http://127.0.0.1:3123/ for the dashboard.
## Configuration
copy `.env.example` to `.env` and edit. every key is optional; a bad value refuses startup.
| key | default | what it does |
|---|---|---|
| `REVIEWZY_PORT` | `3123` | dashboard and mcp port; the bind is `127.0.0.1` always |
| `REVIEWZY_DB` | `~/.local/share/reviewzy/reviewzy.db` | sqlite file (`$XDG_DATA_HOME` respected) |
| `REVIEWZY_BASE_URL` | `http://127.0.0.1:$REVIEWZY_PORT` | dashboard url used in notification links |
| `REVIEWZY_DEV` | unset | set to `1` to drain and respawn the daemon from the checkout on every session start (dev only) |
| `REVIEWZY_TOKEN` | unset | bearer token guarding mcp; set it and it is required |
| `DASHBOARD_PASSWORD` | unset | dashboard login; set it and it is required |
| `ARCHIVE_AFTER_DAYS` | `90` | days an applied entry waits before archiving |
| `NTFY_URL` + `NTFY_TOPIC` | unset | ntfy server and topic; ntfy fires only when both are set |
| `NTFY_PRIORITY` | `3` | ntfy priority, `1` to `5` |
| `WEBHOOK_URL` | unset | generic webhook; posts project, batch id, count, and a dashboard link |
a credential left unset keeps that surface loopback-only with a startup warning.
## Integrations
point an mcp client at reviewzy to start filing:
```json
{
"mcpServers": {
"reviewzy": {
"command": "bunx",
"args": ["reviewzy@latest"]
}
}
}
```
the endpoint speaks the stateless `2026-07-28` mcp revision.
## Comparison
| | reviewzy | Contentrain | gotoHuman | Ditto |
|---|---|---|---|---|
| authorship | human authors, agent files and applies | agent authors, human approves | human edits form fields | team edits a live library |
| code integration | anchors on live strings, no externalization | restructures the repo into a content layer | generic forms, no write-back | no code patch-back |
| hosting | self-hosted | self-hosted | saas only | saas only |
| queue | async queue plus blocking long-poll | queue, git/PR-first review | async queue | none |
the blocking human-in-the-loop mcp family pops a dialog per call: no queue, no persistence, no dashboard.
## FAQ
**How do I approve AI-generated text before it lands in my code?** the agent files text as drafts, you author or approve them on the dashboard, the agent applies.
**Can an AI agent rewrite my CLI help text without i18n keys?** yes. entries anchor on the exact current text; apply-back is a targeted replace.
**How do I bulk-rewrite all user-facing copy in a codebase?** file batches per project, group by project or batch on the dashboard, approve in bulk with a before/after diff, then let a later agent session fetch and apply.
**How do I update the style guide my agents read?** edit it on the dashboard at `/style-guide`. a saved edit reaches agents in up to 60 seconds.
## Development
```sh
bun run check # tsc --noEmit && bun test
```
## License
[AGPL-3.0](LICENSE). self-hosted stays self-hosted; a saas wrapping it publishes its changes.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues