CLI referenceverbatra mcpnew

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

This needs @verbatra/mcp 0.2.0 or newer. Earlier releases do not have it, so upgrade that package if yours is older.

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 mcp loads .env.local and then .env from the working directory before it starts; verbatra-mcp loads neither, so pass any API key in the environment the MCP client spawns it with.
  • verbatra mcp rejects an unknown flag as a usage error and exits 2; verbatra-mcp ignores unknown flags.
  • A startup or config error exits 2 from verbatra mcp and 1 from verbatra-mcp.
  • verbatra mcp force-stops with exit code 130 on a second interrupt during shutdown; verbatra-mcp has no such path.

Flags

FlagArgumentDefaultEffect
--cwd<path>current directoryresolve config and locale files from this directory
--config<path>search for oneload this config file instead of searching for one
--allow-spendnoneoffadvertise 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

ToolWhat it doesRead-onlyCalls a provider
project.snapshotresolved config: source locale, target locales, format, file pattern (files.pattern), provider id, config source, glossary indicatoryesno
status.checkper-locale count of missing, stale, and up-to-date keysyesno
status.diffper-locale list of keys that would be added, re-translated, or orphanedyesno
glossary.getevery glossary term and its translation, secret-redactedyesno
glossary.writeadd, replace, or remove one glossary termnono
lock.statethe lock file's version and, per target locale, its recorded key count plus missing, stale, and up-to-date countsyesno
key.integrityone 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)yesno
key.valueone key's current source and target textyesno
translation.editEntrywrite a manual translation for one key in one locale, gated by placeholder, inline markup, and ICU integritynono
translation.retranslateEntryask the configured provider to retranslate one key in one localenoyes
translation.translatePendingtranslate every missing or stale key across every target localenoyes
review.queuekeys the last run flagged for human review, and whyyesno
usage.summarytoken usage and budget status from the last run, including budget.standing and budget.supportedyesno

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, and key.integrity take an optional locales array (at least one entry) that narrows the result to those locales; key.integrity also takes the key to check.
  • translation.editEntry takes locale, key, and value, with value limited to 20,000 characters.
  • glossary.write takes { term, translation }, with term limited to 200 characters and translation to 2,000; translation: null removes the term. It needs a file-backed glossary (the config's glossary pointing at a JSON file); an inline glossary or none fails with GLOSSARY_NOT_FILE_BACKED.
  • translation.editEntry and translation.retranslateEntry return { accepted, value, reason?, details? }. A refused value carries reason, one of placeholder, markup, icu, degenerate, or empty, and details naming what failed; translation.retranslateEntry adds reviewReasons to an accepted result: the review heuristics' flags for the new value, empty when none fired.
  • lock.state returns exists, and when the lock file exists, its version and per-locale keyCount, missing, stale, and upToDate counts.
  • usage.summary returns the last run's usage and, when a token budget was configured, a budget whose standing is within, stopped-before-ceiling, or reached, and whose supported says whether the counted figure is entirely the provider's own reported usage.

Available from @verbatra/mcp 0.2.1

This needs @verbatra/mcp 0.2.1 or newer. Earlier releases do not have it, so upgrade that package if yours is older.

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/mcp

Running 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.ts

Exit codes

These are the exit codes of verbatra mcp; the standalone verbatra-mcp differs as described in the synopsis above.

CodeMeaning
0stopped cleanly by a single interrupt (Ctrl-C, or SIGTERM)
1stopping failed: closing the server threw during shutdown
2could not start: a config error, @verbatra/mcp not installed, or a usage error
130force-stopped by a second interrupt while shutdown was in flight
Edit on GitHub