Amanuensis
by MikeZenko
README.md
# Amanuensis
A writing app where you and your agent work on the same manuscript without fighting over the pen.
You write in the page. Your agent, wherever it lives (ChatGPT's browser, Chrome), reads the manuscript through WebMCP tools, learns the voice the page is written in, checks its drafts against that voice, and proposes. Every proposal lands in the same review galley the built-in drafting engine uses, scored by the same stylometric engine, and nothing enters the manuscript until you keep it. Discard, or change it before keeping, and the agent can read what you did.
Built for the OpenAI WebMCP Challenge, on top of Amanuensis, a manuscript editor with calibrated voice matching.
## Try it in two minutes
1. Open the live URL in a WebMCP host. ChatGPT's in-app browser works out of the box. Chrome 149+ works after enabling `chrome://flags/#enable-webmcp-testing`.
2. The demo instance pairs your browser automatically and opens the library. Create a draft (or open one) and write a few lines.
3. Tell your agent: *"Read my manuscript and propose one edit that sharpens the opening."* The proposal appears in the review galley with its voice score. **Tab keeps, Esc discards**, or edit the galley text before keeping.
4. To make it personal: *"Build my voice from these samples,"* and paste 150 or more words of your own prose. Pick the new voice from the voice control and every proposal is scored against you instead of Austen or Poe.
## Why WebMCP for this
An agent loose in a writing surface has one move: paste text over yours. WebMCP lets the page hand the agent a narrower, better pen.
- **Proposals, not edits.** No tool mutates the manuscript. Writes come back as takes in the review galley; the writer's keep is the only thing that commits them, and that keep is recorded as feedback the same way it is for the built-in engine.
- **The page holds the voice.** The manuscript carries a stylometric fingerprint of its voice, with a calibration band built from that voice's own samples. Every proposal is scored against it before the writer sees it, and the agent is told the verdict.
- **The outcome is data.** `get_proposals` returns what the writer kept, changed (with their version), or discarded. The agent's next proposal can be better for a reason.
Neither side can do this alone. The writer keeps taste and authority without retyping the agent's ideas; the agent gets structured access, honest telemetry about how its prose reads, and a feedback loop that survives between turns. The collaboration is visible on screen: the galley, the voice gauge in the margin, an activity readout of every tool call, and a version history that records which changes came by whose hand.
## The tools
| Tool | What it does |
| --- | --- |
| `read_manuscript` | Title, full text, word count, and the voice the manuscript is written in |
| `get_outline` | Headings and scene breaks with word counts |
| `get_voice_profile` | The active voice: calibration band, sample coverage, representative sentences, and the other voices on the desk |
| `check_voice` | Score a draft against the voice before proposing it: match score against the calibrated band plus a rhythm-and-variety verdict |
| `create_voice` | Fingerprint a personal voice from the writer's own samples |
| `propose_edit` | Stage a replacement for an exact quote, with a rationale, in the review galley |
| `propose_insertion` | Stage new prose after an exact quote, with a rationale |
| `get_proposals` | Every proposal and its outcome, including the writer's edited version when they changed it |
| `snapshot_version` | Save a named version before suggesting anything sweeping |
| `list_versions` | The version history, with author attribution |
Proposals address the manuscript by exact quote, not by offsets, so they survive the writer typing around them; a quote that no longer exists is rejected at proposal time with an explanation.
## How it is built
- `app/apps/web/src/webmcp/`: the WebMCP layer. `context.ts` wraps `navigator.modelContext.registerTool` (host-agnostic, with a `?mcp=mock` loopback for testing); `tools.ts` registers the ten tools; `install.ts` runs at boot. Tools reach the running app through its client runtime (`runAtomCommand` against the writing RPCs) and the `agentBus` seam.
- `app/apps/web/src/components/writing/agentBus.ts`: the seam between the writing surface and an outside agent, in the same window-bus shape the app already uses. The view publishes what it knows (open draft, voice, versions) and listens for proposals, which it stages as ordinary takes in its review galley.
- `app/apps/server/src/http.ts`: the public-demo mode (`AMANUENSIS_PUBLIC_DEMO=1`). An unauthenticated visitor is minted a one-time pairing token and sent through the ordinary pairing exchange with a narrowed scope set (no terminal). Off by default; every other deployment keeps the pairing wall.
- `harness/`: the Python voice engine the server spawns over ACP. Voice match is function-word cosine similarity plus sentence-rhythm terms against the voice's samples, read against a leave-one-out calibration band; rhythm and variety come from burstiness and MTLD bands. Heuristic, and labeled as such in the UI.
## Run it
Locally (Node 22+, pnpm via corepack, Python 3.11+):
```bash
cd app
corepack pnpm install
corepack pnpm dev # server + web client; open the printed pairing URL
```
Hosted, as one container (server, web client, harness): see `deploy/README.md` for a Docker host, or `render.yaml` for Render. The image seeds the two public-domain voices from `deploy/seed/corpus`.
## What is new for the challenge, and what predates it
Amanuensis existed before the submission period as a personal writing tool: the manuscript editor, the library, version history, the voice engine and its calibration, and the take-review galley. The application shell began as a fork of the MIT-licensed t3code.
Written during the challenge: the WebMCP tool layer and its host wrapper, the `agentBus` seam that stages agent proposals into the real review flow, proposal scoring and the outcome ledger the agent reads back, the public-demo pairing mode, the Render blueprint, and the demo corpus seeding.
## License
[MIT](LICENSE). The t3code notice is preserved at `app/LICENSE`.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues