MCP Tool Reference
The eleven tools stringlane mcp exposes, what each returns, which six write, and why the approval prompt your agent host shows is the gate. Read from the live tool registry at CLI version 0.9.0.
Last updated
Eleven tools, six of which write. The project is fixed when the server starts, and no tool takes a file path, so there is nothing an agent can point at a directory you did not open.
The five that read#
inspect_project#
Every locale, what is missing or stale, and every validation finding: the same report stringlane check gives, including anything your committed baseline suppresses.
With sourceStamps: true, a project where few translations are confirmed has one translation_unconfirmed finding per translation, so inspect_project sends at most 50 findings of each of translation_stale and translation_unconfirmed, with a STAMP_ISSUES_TRUNCATED warning that points at stringlane check --json for the complete list. The usage findings are capped the same way.
read_translations#
What a key actually says, in the locales you ask for. Decoded, unescaped, and with plural categories separated, so the agent sees the value rather than the file's encoding of it.
prepare_translation_plan#
Exactly what needs translating, each item with its base value, description, placeholders and plural categories, plus your product and branding context. Also reports how many of the keys have no description, so an agent can offer to write those first.
Besides missing translations, a plan offers stale ones: a translation whose base text moved since it was confirmed, labelled source-changed, and one an earlier apply wrote whose context or description moved since, labelled context-changed. With sourceStamps: true, a source-changed item carries the base text it was confirmed against as previousSourceValue, beside the new sourceValue, when the stamps file holds it, so an agent can make the smallest edit that fits. In an .xcstrings catalog, a previousSourceValue equal to sourceValue means Xcode marks the unit for review while its base text has not changed; applying the same text clears that flag. A translation never confirmed is reported by stringlane check, not offered here.
An empty plan carries emptyReason, because zero items is only good news when the reason is complete. The other four values, incomplete, no-targets, empty-source and unroutable, each point at a different fix: see the reasons table. With sourceStamps: true, complete means nothing is left to translate, and stringlane check can still list translations a person has to confirm.
validate_translations#
Checks proposed translations without touching a file: placeholders, ICU structure, plural completeness, guarded words, length. Reports every problem rather than the first.
read_key_usage#
Where your code calls a key, and which keys nothing calls. Either the call sites of one key, or the project's not-referenced set.
It exists so your agent does not grep your tree and get a worse answer. The merged key a plan spells is not always the string your code contains (a multi-origin project prefixes it, and the runtime never sees that prefix), so a search for the wrong spelling reports every key as unused, which looks exactly like a filthy codebase. This tool knows each framework's call shapes and applies the project's own ignore rules.
It refuses when it could not read your source, rather than reporting everything dead. "I read nothing" and "everything is dead" produce an identical empty list, and only one of them is safe to act on. Its not-referenced list is what the scanner saw and not a verdict: see Unused keys.
The six that write#
Your agent asks you before any of them runs, the same way it asks before editing a file. That prompt comes from your coding agent host, not from StringLane, and it is the real gate. Everything StringLane adds is underneath it.
Earlier versions also required an authorize: true argument on each writing tool. That is gone, and it is worth saying why, because it sounds like a protection being removed. It was not one. The agent filled that field in itself, so it was the agent asserting that you had decided, which is not the same as you deciding. In two recorded incidents an agent that met the refusal, and still had a job to finish, wrote the locale files with a script instead: a path with no placeholder checks, no lock, and no idea that i18next keeps descriptions in a different file. The gate did not stop a write; it moved the write somewhere nothing could check it. A leftover authorize argument from an older caller is ignored, never rejected.
Every write lands as one atomic commit under a per-project lock, so a failure leaves your project exactly as it was, and git diff shows you everything that happened.
apply_translations#
Writes validated translations into the project's locale files. It:
- only accepts keys, locales and plural categories the plan offered, so there is no way to ask it to translate something else;
- re-runs every check
validate_translationsruns, so a passing validation earlier in the conversation is not permission; - refuses if the project changed since the plan was made, rather than overwriting work it never read;
- builds every proposed file in memory and re-parses it before touching anything, so one broken file means no file is written.
If it refuses, it says which of those it was.
describe_keys#
Stores what a key is for, so every future translation is made with context rather than a guess. Descriptions go where your format keeps them: the ARB @key block, an iOS or Android comment, or .stringlane/metadata.yaml for i18next. It also reports which of the keys it documented nothing in your code calls, so an agent stops writing descriptions for dead ones.
In a project that spreads its base locale across several files, such as a Gradle project with several modules, it refuses with VALIDATION_FAILED and writes nothing; a project split into i18next namespaces is not refused. Adding a description to a key that is already translated marks that translation out of date, which is correct, because it was made without the context, but it means work. Describe before translating.
add_locale#
Creates the file for a locale the project does not have yet, in the right place and the right format. The file is empty apart from the locale marker, on purpose.
It does not make your app offer the language. That half lives in your own source (a supportedLocales literal, an Info.plist, an Xcode project, a locale_config.xml, a supportedLngs array, a language picker), and every one of them is optional, so none can be assumed. Missing one produces a fully translated locale the app never offers. The plugin's locale-wiring skill carries that per platform: see Agent hosts.
add_key#
Adds new keys and their source strings to the base locale only, for keys your code references but the project does not have yet. Every other locale then reports each one as missing in the next plan, and the ordinary plan, validate and apply chain fills it.
It exists because apply_translations refuses any key the plan did not offer, so an agent that had written t('checkout.title') into a component had no honest way to make that key exist.
It takes the whole set of keys in one call, and that is one atomic write: either every key lands or none does. Each key can carry a description of what the string is for, stored where your format keeps one: the ARB @key block, an iOS or Android comment, or .stringlane/metadata.yaml for i18next, where it is a second file committed alongside the keys. An empty description is refused. A key added without one is reported by the next prepare_translation_plan under keysWithoutDescription.
It refuses a key name your format cannot carry, before it breaks a build. checkout.title is legal ARB and i18next, a broken Android resource name (aapt2 rejects it), and in a Flutter project, detected by pubspec.yaml, a broken Dart getter; a leading underscore is refused in Dart projects. One bad name refuses the whole set, so a partial write is not a state you can reach.
In a project that spreads its keys across several files, i18next namespaces included, add_key refuses with VALIDATION_FAILED and writes nothing, because it cannot yet choose which file a new key belongs in. The desktop app can add the key; then plan again.
pluralize_key#
Gives a key your project already has its plural forms, for a count_without_plural finding: a string such as {{count}} genres that i18next shows for every count, "1 genres" included. i18next projects only; a project in any other format is refused.
It exists because add_key refuses a key that already exists, so without it the only fix is a hand edit of the locale file, which is the edit the write guard is there to catch.
It takes the key, exactly as the finding reports it, namespace included (translation:savedSearches.nGenres), and forms: the text for each plural category your base language uses, one and other for English. It replaces the key's single string with those forms, in the base file only. A missing or empty form, a form your base language does not use, a form that drops a placeholder the current text has or adds one it does not, a form that writes a placeholder differently from the current text ({{count}} where it has {{- count}}), and a key that already has plural forms are each refused, and nothing is written. The one form may leave out the count, as in one genre; the other form may not.
It returns the file it changed, the forms it wrote, and owedByLocale: each other locale that now owes forms, and which ones, such as "ru": ["one", "few", "many", "other"]. Those locales keep their old string, and the next prepare_translation_plan offers each owed form, so the agent can go straight on to translate them.
There is no preview: the call writes. Because it replaces text, it is marked as a destructive tool, so your host's approval prompt is the moment to look, and the forms are in the diff afterwards. From the terminal, stringlane pluralize does the same, and asks first.
set_project_context#
Records what the product is, how it should sound, per-locale notes, and words that must never be translated. Writes to stringlane.yaml, so the desktop app and the CLI both honour it afterwards.
Prompts#
The server also exposes a review-translations prompt, which asks the agent to run inspect_project and work through the findings it can actually verify. It deliberately does not ask the agent to judge how your German reads; that is not something this tool can check twice and get the same answer.
What the server does not do#
It has no tool for stringlane accept, on purpose. Confirming that a translation still fits its changed base text is a person's call, so it stays a command a person runs, and the stamps file it writes is reviewed in the pull request.
It contacts no network service. It runs on your machine, talks over stdin and stdout to the agent that started it, and holds no API key: in agent mode the model is your agent's own.
Frequently asked questions
- How many StringLane MCP tools can write to my files?
- Six of the eleven: apply_translations, add_key, pluralize_key, describe_keys, add_locale and set_project_context. Your agent asks you before any of them runs, the same way it asks before editing a file. That prompt comes from your coding agent host, not from StringLane, and it is the gate.
- Can an MCP tool read a directory I did not open?
- No. The project is fixed when the server starts, and no tool takes a file path, so there is nothing an agent can point somewhere else.
Free · In your coding agent
Run the same checks from your coding agent
Paste this into Claude Code, Cursor, Codex or five other agents. It sets up the CLI and MCP server for this project, proposing each command and waiting for your yes.
Read https://stringlane.app/docs/cli/agent-setup.md and set up StringLane for this project.Or install it yourself:
npm install -g @stringlane/cli