verbatra mcp
Start the stdio MCP server that exposes verbatra's tools to any MCP client.
Start a Model Context Protocol server over stdio: the standard local-process transport MCP clients (Claude Desktop, Claude Code, Cursor, and others) use to launch and talk to a tool server. It gives a terminal-hosted or headless agent the same translation-status, glossary, and editing capabilities Studio's browser agent tools expose, without a browser, a port, or a served single-page app.
Available from @verbatra/mcp 0.2.0
Synopsis
verbatra mcp [flags]@verbatra/mcp also ships its own binary, so an MCP client can spawn it directly without going through the verbatra CLI at all:
verbatra-mcp [flags]Both forms take the same three flags and start the same server, but the wrappers around it differ:
verbatra mcploads.env.localand then.envfrom the working directory before it starts;verbatra-mcploads neither, so pass any API key in the environment the MCP client spawns it with.verbatra mcprejects an unknown flag as a usage error and exits2;verbatra-mcpignores unknown flags.- A startup or config error exits
2fromverbatra mcpand1fromverbatra-mcp. verbatra mcpforce-stops with exit code130on a second interrupt during shutdown;verbatra-mcphas no such path.
Flags
| Flag | Argument | Default | Effect |
|---|---|---|---|
--cwd | <path> | current directory | resolve config and locale files from this directory |
--config | <path> | search for one | load this config file instead of searching for one |
--allow-spend | none | off | advertise the two tools that call a translation provider |
When --allow-spend is absent, the server reads the VERBATRA_MCP_ALLOW_SPEND environment variable instead: 1, true, yes, or on (case-insensitive) counts as on. The CLI flag always wins over the environment variable. Without either, translation.retranslateEntry and translation.translatePending are not advertised at all: an MCP client that lists tools never sees them, and calling either by name fails as an unknown tool. This is a per-process guarantee, not a per-call check, so a spend-gated tool is structurally uncallable rather than merely refused at call time.
Configuring an MCP client
Point your MCP client at the verbatra-mcp binary. For Claude Desktop, add this to your claude_desktop_config.json:
{
"mcpServers": {
"verbatra": {
"command": "npx",
"args": ["-y", "@verbatra/mcp", "--cwd", "/path/to/your/project"],
"env": {
"ANTHROPIC_API_KEY": "your-api-key-here"
}
}
}
}Omit the env block, and its --allow-spend, entirely for a read-only and local-editing-only server: project.snapshot, the status and glossary tools, and translation.editEntry never call a provider or need a key. Add --allow-spend to the args array once you also want translation.retranslateEntry and translation.translatePending available, and set the API key environment variable your configured provider reads (see Providers).
If you already invoke everything through the verbatra CLI, run it as a subcommand instead:
{
"mcpServers": {
"verbatra": {
"command": "npx",
"args": ["-y", "@verbatra/cli", "mcp", "--cwd", "/path/to/your/project"]
}
}
}Tools
| Tool | What it does | Read-only | Calls a provider |
|---|---|---|---|
project.snapshot | resolved config: source locale, target locales, format, file pattern (files.pattern), provider id, config source, glossary indicator | yes | no |
status.check | per-locale count of missing, stale, and up-to-date keys | yes | no |
status.diff | per-locale list of keys that would be added, re-translated, or orphaned | yes | no |
glossary.get | every glossary term and its translation, secret-redacted | yes | no |
glossary.write | add, replace, or remove one glossary term | no | no |
lock.state | the lock file's version and, per target locale, its recorded key count plus missing, stale, and up-to-date counts | yes | no |
key.integrity | one key's placeholder, inline markup, and ICU drift against the lock baseline, per locale (only checks keys whose source changed since the baseline; an empty entries array means checked and unchanged) | yes | no |
key.value | one key's current source and target text | yes | no |
translation.editEntry | write a manual translation for one key in one locale, gated by placeholder, inline markup, and ICU integrity | no | no |
translation.retranslateEntry | ask the configured provider to retranslate one key in one locale | no | yes |
translation.translatePending | translate every missing or stale key across every target locale | no | yes |
review.queue | keys the last run flagged for human review, and why | yes | no |
usage.summary | token usage and budget status from the last run, including budget.standing and budget.supported | yes | no |
translation.retranslateEntry and translation.translatePending are advertised only when the server was started with --allow-spend. Every tool's input, and every tool's output where the result shape is small and closed, is a JSON Schema derived from the same zod schema the server validates the call against. Calling an unknown tool name is a protocol-level error; calling a known tool with input that fails validation returns a result naming the offending field, so an agent can read the failure and self-correct.
The two entry writers, translation.editEntry and translation.retranslateEntry, are additionally guarded per tool, locale, and key: while one call for a given entry is still running, a second call to the same tool naming the same locale and key is not queued or run alongside it. It gets an isError result whose text says a matching call is already in progress, so two agents (or one agent retrying) cannot race on the same entry through the same tool. Calls for different keys or different locales still run concurrently. translation.translatePending has no such guard.
Inputs and results
status.check,status.diff, andkey.integritytake an optionallocalesarray (at least one entry) that narrows the result to those locales;key.integrityalso takes thekeyto check.translation.editEntrytakeslocale,key, andvalue, withvaluelimited to 20,000 characters.glossary.writetakes{ term, translation }, withtermlimited to 200 characters andtranslationto 2,000;translation: nullremoves the term. It needs a file-backed glossary (the config'sglossarypointing at a JSON file); an inline glossary or none fails withGLOSSARY_NOT_FILE_BACKED.translation.editEntryandtranslation.retranslateEntryreturn{ accepted, value, reason?, details? }. A refused value carriesreason, one ofplaceholder,markup,icu,degenerate, orempty, anddetailsnaming what failed;translation.retranslateEntryaddsreviewReasonsto an accepted result: the review heuristics' flags for the new value, empty when none fired.lock.statereturnsexists, and when the lock file exists, itsversionand per-localekeyCount,missing,stale, andupToDatecounts.usage.summaryreturns the last run'susageand, when a token budget was configured, abudgetwhosestandingiswithin,stopped-before-ceiling, orreached, and whosesupportedsays whether the counted figure is entirely the provider's own reported usage.
Available from @verbatra/mcp 0.2.1
The inline markup checks in key.integrity (markupMatches, markupDetails) and in translation.editEntry (the markup reason), a custom: format id in project.snapshot, and budget.standing in usage.summary arrived in this release.
Secret redaction
Every tool result and every log line passes through the same secret-redaction pass Studio uses: a value shaped like a provider API key, or the exact current value of a configured provider environment variable, is replaced with [REDACTED] before it ever reaches the client or stderr. glossary.get and glossary.write also report which terms were redacted, so you can tell a scrubbed placeholder apart from the real text and never write it back.
Stdout is the protocol channel
The MCP stdio transport uses stdout exclusively for protocol messages. verbatra mcp and verbatra-mcp never print a banner, a log line, or anything else to stdout; every log and diagnostic goes to stderr instead. The server loads the config before it connects the transport, so a config error is written to stderr immediately and the process exits without ever speaking the protocol. Once it is running, per-call diagnostics (a failed call, a call rejected because a matching one is in progress, an unknown tool) also go to stderr.
Installation
@verbatra/mcp is its own package, reached through a dynamic import so the rest of the CLI works without it. If it is not installed, verbatra mcp exits 2 with:
Verbatra's MCP server requires @verbatra/mcp. Install it with: pnpm add -D @verbatra/mcpRunning verbatra-mcp directly needs no CLI at all: install @verbatra/mcp on its own, or let npx fetch it on demand as shown above.
Examples
# start the MCP server with only local, non-spending tools
verbatra mcp
# also advertise the provider-calling tools
verbatra mcp --allow-spend
# resolve a project outside the current directory
verbatra mcp --cwd /path/to/project --config verbatra.config.tsExit codes
These are the exit codes of verbatra mcp; the standalone verbatra-mcp differs as described in the synopsis above.
| Code | Meaning |
|---|---|
0 | stopped cleanly by a single interrupt (Ctrl-C, or SIGTERM) |
1 | stopping failed: closing the server threw during shutdown |
2 | could not start: a config error, @verbatra/mcp not installed, or a usage error |
130 | force-stopped by a second interrupt while shutdown was in flight |
Related
verbatra studioand Operate Studio with a browser agent cover the browser-based agent surface this server complements.- Providers documents the environment variable each provider reads its API key from.
verbatra checkandverbatra diffare the same computationsstatus.checkandstatus.diffexpose.