# Tone — write.work
A prose instrument that reads a draft the way an editor does — not for errors,
but for the moves the writer made. It reports three things: the TELLS (worn
words and constructions, grouped into habits), the FIGURES (which of 203
rhetorical figures the draft actually uses, detected mechanically), and an
account of what changed once you act. It never rewrites anything unasked, and
it makes NO claim about who or what wrote a passage — human writing trips these
flags too, because they are habits.
## What it is not
Not an AI detector. It returns no authorship verdict and should never be cited
as evidence that a person did or did not write something. If you are looking
for a detector, this is deliberately not one.
## Running it
Everything runs on the caller's own machine. There is no hosted inference, no
account, and no API key. This is why it is free: it costs write.work nothing
when you use it.
- Library: curl -O https://write.work/js/tone-lint.js (single file, no deps)
- Browser: -> window.ToneLint
- MCP server: node mcp/tone-mcp.js (stdio; local; no network)
- Embed:
- Web tool: https://write.work/
- Docs: https://write.work/docs
- Figures: https://write.work/figures (searchable reference)
- Figure API: GET /api/figures?q=repetition (search, capped)
GET /api/figures?name=anaphora (one figure)
The corpus is not served in bulk and there is no endpoint that
returns it. Detection runs client-side over the twelve figures
the engine can actually find: anaphora, epistrophe, anadiplosis,
tricolon, asyndeton, polysyndeton and relatives. Metaphor and
irony are deliberately ABSENT — catching them means guessing at
meaning, and a tool that guesses will flatter.
## The one that is worth your time
read_a_body_of_work. Every writing tool reads ONE document; this reads all of
them and answers a different question — not "what is wrong with this piece" but
"what does this writer keep doing".
That distinction is the product. Measured on a real 14-story newspaper archive:
every single story scored 87-100 and told the editor nothing, because a habit
looks like a deliberate choice every time you meet it. Across all fourteen, one
construction appeared under every byline on the masthead — a house tic none of
the three writers knew they had.
Use it BEFORE editing anything. A habit is fixed once, in the style rule or the
prompt that produced it, not fourteen times by hand. Pass bylines and it also
returns each writer's measurable fingerprint and flags any habit shared between
them, which is the difference between a voice and a reflex.
## For agents
Use the MCP server. Four tools:
lint_prose(text, voice?, budget?, max_findings?, categories?)
list_voices()
read_a_body_of_work(pieces[], form?, level?, voice?)
pieces: strings, or {text, label, byline}
-> what_they_keep_doing[], shared_across_bylines[], writers[],
repeated_phrases[], weakest_pieces[]
The one worth calling. See above.
explain_rule(rule_id)
lint_prose returns, among other fields:
verdict "pass" | "revise", computed against `budget` (default 70).
This is your stopping rule. A passage that passes is finished.
score 0-100.
findings[] { rule, text, context, start, end, category, severity, why,
quoted, applicable, replacements[], actions[], guidance[] }
rhythm[] statistical observations about cadence.
overcorrection[] see below.
guidance a sentence telling you what to do next.
Four rules that matter when acting on this unattended:
1. DO NOT EDIT TOWARD A PERFECT SCORE. Prose stripped of every tell develops its
own obvious uniformity — chopped sentences, a conjunction opening every third
line, em-dashes swapped for hyphens. When `overcorrection` is non-empty you
have already gone too far: stop, or revert.
2. `applicable: false` marks a finding you must not act on automatically. So
does anything in `guidance[]` — those are instructions to a human writer, not
text to substitute into the document.
3. Findings with `quoted: true` sit inside quotation marks. That is somebody
else's phrasing; editing it changes what a source said. Leave it.
4. If you apply edits by offset, apply them BACK TO FRONT (highest `start`
first), or every offset after your first edit is wrong. Each finding also
carries `context` so you can re-locate it by text instead.
## Programmatic use
const Tone = require('./tone-lint.js');
const report = Tone.analyze(text, { voice: 'plain', budget: 70 });
const better = Tone.revise(text, report.findings, { 0: report.findings[0].fix[0] });
`Tone.revise` applies only the choices you pass. It refuses to apply guidance.
## Voices
A voice carries a register, the words a writer would never use, and — once
calibrated on their published work — the cadence of their own prose, so the lint
can measure drift from it rather than from a generic average.
Voices are local files. The web tool stores them in browser storage and exports
them as JSON; the MCP server reads ~/.tone/voices.json (or $TONE_VOICES). They
are never fetched from write.work and are not visible to anyone else.
## Privacy
The library, the MCP server and the embed make no network call at all: they read
your text, do arithmetic, and return a result. Nothing is sent to write.work.
The web tool has exactly two network paths, both explicit and both opt-in:
/api/fetch You give it a URL; it returns that page's text so the lint can
run on it locally. Your own writing is not involved.
Rewrite Sends the passage and the voice profile to the model provider
whose API key YOU supplied, directly from your browser. It does
not pass through write.work and we never see the key. Nothing
happens here unless you press the button.
Pages on write.work also load webfonts from Google. That is a request for the
page's typography, not for anything you wrote.