Guides
Coding agents
An agent writing your app also writes the text in it. There are three ways to hand it the cycle, and they answer different questions: run the commands, learn the rules, or read the docs.
Three channels, three questions
| Channel | What it answers | What it needs |
|---|---|---|
| MCP server | Run the cycle: diagnose, wrap the text you already wrote, extract, translate | One line in your MCP client |
| Agent Skill | Write the code right the first time: the rules that keep the cycle safe | A file copied into your project |
| llms.txt | Find the right page: an index of these docs in plain markdown | Nothing, it is already served |
They stack. The skill teaches the rules, the server does the work, and the index is what an agent reads when it needs more than either. None of them can approve a machine translation, and the end of this page says why.
The MCP server
@verbaly/mcp exposes the cycle as tools over the Model Context Protocol, so an agent runs it without touching a shell. It works with any MCP client, Claude Code and Cursor included.
The server reads your verbaly.config from the directory it starts in. Pass --root when that is not your project, and every tool also takes a root of its own for a monorepo with more than one app.
The tools
They are listed in the order an agent meets a project: set it up, find out what is wrong, wrap the text that is already there, then run the cycle over it. That is the order the server hands them over in, not just the order of this page.
| Tool | What it does | Writes? |
|---|---|---|
| verbaly_init | Creates your settings file and your language files, and works out which bundler or framework you use so it can name the one line you still have to add. A language given as pt_BR is written pt-BR, and it says so | Yes |
| verbaly_doctor | Checks the whole setup at once: config, catalogs, the plugin for your framework, the generated types, files it could not read and every translation the gate would reject | No |
| verbaly_wrap | Finds text written straight into your JSX and wraps it so the compiler can pick it up. It reports by default, and text it cannot handle safely is listed rather than rewritten | Yes |
| verbaly_extract | Scans your source, adds new messages to the catalogs and refreshes the generated types. A key it prunes takes its draft along, and the answer names, with file and line, any file it could not read, any key written with two texts, any t it cannot read because it goes by another name, any source text your code wrote over and any value a translator would see without a name. When it creates verbaly.lock, moves an older state file into it or rewrites it whole after a merge conflict, the answer says so, and the agent can tell you to commit it | Yes |
| verbaly_status | Coverage per language, plus how many machine translations are waiting for a human and how many translations were written for an older text. A state file it cannot read is reported in the answer, never as a failure | No |
| verbaly_missing | Everything the CI gate would fail on: missing translations, unknown keys, keys written with two texts and translations that exist but cannot render what the source renders. It also lists what never fails it: keys only a translation has, a catalog that says something your code does not, and translations written for an older text. Every path in it is relative to your project | No |
| verbaly_translate | Fills the gaps through your configured provider, saving everything as a draft. A message someone wrote while it worked is left as written, and the answer names it | Yes |
| verbaly_write_drafts | Saves the translations the agent wrote itself. Each one is checked like a machine translation against the text your code ships, written only for keys your source language has, and marked as a draft for you to review, with a note of the text it was written for. It keeps what is already translated unless told to rewrite it | Yes |
| verbaly_drafts | Every machine translation still waiting for you, each next to the sentence it came from, so you can read both before deciding | No |
The ones that only read say so to the client, so an agent can look at the state of your project without permission to change anything. The ones that write tell you first: extract and translate take a dryRun that reports what would happen, and wrap reports unless you ask it to apply.
Every tool answers twice: a sentence for a person reading the transcript, and the same answer as data. An agent asking for coverage gets the numbers themselves, not a line to pick apart, so it keeps working when we word something differently.
A tool that fails comes back as a message the agent can act on, never as a crashed server. A missing config file says so, and so does a catalog that is not valid JSON. A state file a merge left broken only adds a line to the answers that read it, and the tools that would write drafts stop before writing anything.
Translating is the one step that can be interrupted halfway, because it goes out over the network. When a piece of it does not come back, the rest is still saved and the answer names exactly which messages are left, so asking again costs only what is missing.
What it can read
Every tool above works in keys and counts, which is enough to tell you that fourteen translations are missing and not enough to show you one of them. Two resources close that: an agent reads them by address, so looking at your project does not use up one of its actions.
| Address | What it holds |
|---|---|
| verbaly://config | Which language you write in, which ones you translate to, where your language files live and whether the language shows in your addresses |
| verbaly://catalog/{locale} | Every message of one language, exactly as your app ships them: a text your code owns reads as your code has it. An empty value means nobody has translated it yet |
This is what makes reviewing a machine translation possible through an agent. Before, you were handed a key and asked to approve it, which is not a review. Now you see the sentence it came from next to what the machine wrote, and the decision stays yours.
The Agent Skill
A server tells an agent what it can run. A skill tells it what to write. The Verbaly Agent Skill lives in the repository and carries the cycle plus the rules that keep it safe, so an agent stops inventing catalog keys by hand.
The rules it carries are the ones that are easy to get wrong from the outside.
- Keys are generated, or come from the catalog the project already has. The agent never invents one.
""in a catalog means untranslated, not empty on purpose.- Placeholders and tags have to survive a translation word for word.
- A link goes through a named tag plus a links map, never a literal
<a href>inside a message. - A translation it writes itself is saved with
verbaly_write_drafts, never typed into a catalog.
llms.txt
This site serves /llms.txt, a markdown index of every documentation page with a line on what each one covers. It is generated from the same sidebar you are looking at, so it cannot drift from the real docs.
It is for the agent that reads before it writes. Point yours at it when it needs the whole map, and at a single page when it needs one answer.
Why an agent cannot approve its own translation
Everything verbaly_translate produces is a draft, and so is everything an agent saves with verbaly_write_drafts, and drafts do not ship: with verbaly check --drafts in CI, the build keeps failing while any remain. Accepting one is verbaly review --approve or the local review server, and neither is exposed to agents: an agent approving its own translation is the safeguard approving itself.