# RaiGanja/kaeris-mcp [Health: Active]

**Category:** 🌎 Translation Services  
**Repository:** https://github.com/RaiGanja/kaeris-mcp  
**GitHub Stars:** 0  
**Views:** 5  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/raiganja-kaeris-mcp

## Description
AI localization: translate an app's string files into 46 languages (JSON, .arb, .po, .strings, Android XML, …) with placeholder-safe, reproducible output — from Claude/Cursor.

## Tools
Capabilities this server exposes over MCP:

- **kaeris_list_languages** — List every target language KAERIS can translate into.

    Returns a mapping of language code → English name (e.g. {"es": "Spanish", ...}).
    Use the codes with kaeris_translate / kaeris_translate_file.
    
- **kaeris_scan_repo** — Discover a repo's i18n setup: which locale files exist, what the base
    (source) language and target languages are, and which framework/convention
    is in use. Purely local — reads the filesystem, makes no API call.

    Use this FIRST when asked to "look at/audit/fix this repo's i18n" and you
    don't already know the source file — it tells you where to point
    kaeris_status / kaeris_list_missing_keys / kaeris_add_language.

    Supports i18next/react-i18next (locales/<lang>/*.json), next-intl
    (messages/<lang>.json), vue-i18n (src/locales/<lang>.json), Flutter ARB
    (lib/l10n/*.arb), Android (res/values[-<lang>]/strings.xml), iOS
    (<lang>.lproj/Localizable.strings), gettext (locale/<lang>/LC_MESSAGES/*.po),
    Xcode String Catalogs (Localizable.xcstrings — one file holding every language,
    read out of the catalog itself), XLIFF (.xliff/.xlf), Java/Spring .properties,
    .NET RESX, Mozilla Fluent (.ftl), Markdown/MDX, plus plain
    <lang>.json/.yaml/.csv files anywhere else. Skips node_modules,
    .git, build/dist output and other noise; bounded so it's safe on large repos.

    Args:
        path: directory to scan (default: current directory).

    Returns:
        { "root": "<abs path scanned>",
          "locale_files": ["locales/en.json", "locales/de.json", ...],
          "base_lang": "en",            # best guess, or null if none found/ambiguous
          "target_langs": ["de", "fr"], # languages present other than base_lang
          "framework": "i18next",       # best guess, or null if unrecognized
          "file_count": 2 }
    
- **kaeris_status** — Completeness/health check for every target locale against a source file
    — the "i18n firewall". Works for EVERY supported format: JSON is checked fully
    offline; other formats (.arb/.strings/.po/.xml/.ftl/…) are parsed by the backend
    /api/parse endpoint (no translation, no cost).

    For each target language reports missing keys, extra/stale keys,
    placeholder mismatches (e.g. "Hello {name}" -> "Bonjour" loses {name}, or
    "{name}" -> "{nom}" which crashes str.format()/ICU at runtime), and the same
    quality detectors the CLI firewall gates a merge on: number drift
    ("Delete 5 files" -> "Delete 50 files"), lost inline tags, broken entities
    or escapes, missing ICU/CLDR plural categories. Those come back under
    "faults" (RED — they set ok=false) and "warnings" (YELLOW — advisory).
    Use this
    to find out what's broken before deciding whether to call
    kaeris_list_missing_keys (existing locale, some keys missing/broken) or
    kaeris_add_language (locale doesn't exist yet at all).

    Args:
        source: path to the base-language locale file, e.g. "locales/en.json" or
                "lib/l10n/app_en.arb" (typically kaeris_scan_repo's base_lang file).
        langs: target language codes to check, e.g. ["de", "fr"]. If omitted,
               falls back to kaeris.json's "langs" if present, else auto-discovers
               sibling <lang> files next to the source.
        out: directory containing the target locale files (default: source's own
             directory; each target is expected at "<out>/<lang><source-ext>",
             e.g. de.json for en.json, de.arb for en.arb).

    Returns:
        { "ok": bool, "missing": {lang: [keys]}, "extra": {lang: [keys]},
          "placeholder_issues": [{"lang","key","missing","added"}, ...],
          "faults": [{"lang","key","severity":"error","msg"}, ...],    # RED — ok is false
          "warnings": [{"lang","key"?,"severity":"warn","msg"}, ...],  # YELLOW — advisory
          "missing_files": [lang, ...],  # target language has no locale file at all
          "counts": {"missing": {lang: n}, "extra": {lang: n},
                     "placeholder_issues": n, "faults": n, "warnings": n},
          "note": "...",                 # present only when a list was shortened
          "source", "out_dir", "langs" }

    "ok", "counts" and "missing_files" are always exact and complete. The lists are EXAMPLES —
    at most 50 entries each — because a status call over a large repo otherwise returns a
    six-figure token bill in a single reply. Use kaeris_list_missing_keys (paged) for the full
    picture of one language.
    
- **kaeris_list_missing_keys** — The exact keys missing or placeholder-broken in ONE target locale, with
    their source-language text, so an agent knows exactly what to translate/fix.
    Every supported format works (JSON offline; others via the backend /api/parse,
    no cost) — use kaeris_add_language or kaeris_translate to actually fill them in.

    The answer is PAGED: "missing_count" is always the exact total, while "missing" /
    "missing_values" carry one page of it (default 200 entries, and never more than about
    15k tokens' worth). A whole locale file returned in one tool result is not a long answer,
    it is the end of the agent's session — measured 16.08.2026: a 2 MB source file, the size
    the product accepts, produced 667 000 tokens in a single reply. When there is a next page,
    "page.next_offset" says where it starts.

    Args:
        source: path to the base-language locale file, e.g. "locales/en.json" or "app_en.arb".
        lang: the target language code to inspect, e.g. "de".
        out: directory containing the target locale file (default: source's own
             directory; expected at "<out>/<lang><source-ext>", e.g. de.json / de.arb).
        limit: how many missing keys to return in this call (default 200, max 2000).
        offset: where to start — pass "page.next_offset" from the previous call.

    Returns:
        { "lang": "de", "missing_file": bool,
          "missing_count": 1234,                       # ALWAYS the exact total
          "missing": ["<dotted.key>", ...],            # one page of them
          "missing_values": {"<dotted.key>": "<source text>", ...},   # the same page
          "page": {"total", "returned", "offset", "complete", "next_offset"?, "note"?},
          "extra": ["<dotted.key>", ...], "extra_count": 12,
          "placeholder_issues": [{"key","missing","added"}, ...], "placeholder_issue_count": 3 }
    
- **kaeris_add_language** — Bootstrap a BRAND-NEW target locale by translating the entire source file
    into it and writing the result. Use this when kaeris_scan_repo/kaeris_status
    show a language with no locale file at all yet (in "missing_files").

    This CALLS THE TRANSLATION API (same as kaeris_translate_file) — unlike
    kaeris_scan_repo/kaeris_status/kaeris_list_missing_keys, which are local-only
    and free. For an EXISTING locale that's just missing a few keys, prefer
    kaeris_translate_file(..., only_new=True) so you don't re-translate the whole file.

    Args:
        source: path to the base-language locale file, e.g. "locales/en.json".
        lang: the new target language code to create, e.g. "de".
        out: output directory (default: source file's own directory).
        tone: "formal" or "casual" to steer register; "" (default) is neutral.
        icu: True if the source uses ICU MessageFormat (plurals/select), so the
             model preserves the syntax instead of translating it.
        keep: optional list of terms to NEVER translate (brand/product names).
        context: one line about what the app IS, e.g. "a mobile bank for teenagers" or
              "a wildlife documentary app". The model uses it to pick the right sense of
              ambiguous strings — "Bank" becomes Ufer in the second and Bank in the first.
              You are reading the repo, so you know this: pass it. Max 300 chars.
        api_key / openrouter_key: optional auth overrides. PREFER the KAERIS_API_KEY /
              KAERIS_OPENROUTER_KEY environment variables: a key passed as a tool argument is
              written into the conversation transcript and into the MCP client's logs, where
              it outlives this task.
        job_id: leave empty to start a translation. If a previous call came back with
              {"status": "running", "job_id": ...}, pass that id (with the same source/out) to
              collect the finished result instead of paying for the work twice.

    Returns:
        { "written": ["<out>/<lang>.json"], "lang": "de", "languages": ["de"] }
        or, if the server is still working: { "status": "running", "job_id": "...", "note": ... }
    
- **kaeris_translate** — Translate a set of UI strings into one or more languages.

    Format-aware and placeholder-safe: values like "Hello, {name}" keep their
    placeholders intact. Non-string values are passed through unchanged.

    Includes Translation QA so you can trust the output: it flags translations that
    dropped a placeholder or that grew long enough to risk overflowing the UI. With
    verify=True it also back-translates each result into back_lang so you can check the
    meaning is right even in a language you can't read.

    Args:
        strings: key → source-text pairs, e.g. {"greeting": "Hello", "save": "Save"}.
        target_languages: language codes from kaeris_list_languages, e.g. ["es", "fr", "ja"].
        keep: optional list of terms to NEVER translate — kept verbatim in every language
              (brand/product names), e.g. ["KAERIS", "GitHub"].
        context: one line about what the app IS, e.g. "a mobile bank for teenagers" or
              "a wildlife documentary app". The model uses it to pick the right sense of
              ambiguous strings — "Bank" becomes Ufer in the second and Bank in the first.
              You are reading the repo, so you know this: pass it. Max 300 chars.
        verify: back-translate each result into back_lang to confirm meaning (uses more tokens).
        back_lang: language for the verify back-translation (default "en").
        tone: "formal" or "casual" to steer register; "" (default) is neutral.
        icu: True if values may contain ICU MessageFormat (plurals/select) so the
             model preserves the syntax instead of translating it.
        reuse: optional {lang: {key: previous_translation}} translation-memory map —
               strings that are unchanged from a prior run are reused verbatim
               server-side and only new/changed strings are actually translated.
        api_key / openrouter_key: optional auth overrides. PREFER the KAERIS_API_KEY /
              KAERIS_OPENROUTER_KEY environment variables: a key passed as a tool argument is
              written into the conversation transcript and into the MCP client's logs, where
              it outlives this task.

    Returns:
        { "translations": { "<lang>": { "<key>": "<translation>", ... }, ... },
          "placeholder_warnings": { "<lang>": { "<key>": ["{name}", ...] } },  # if any placeholders were lost
          "overflow_warnings":   { "<lang>": [ {"key","src","tr","pct"}, ... ] },  # if UI-overflow risk
          "back_translations":   { "<lang>": { "<key>": "<back-translation>" } },  # only if verify=True
          "failed_languages": [...]  # present only if some languages could not be translated
        }
    
- **kaeris_translate_file** — Translate a strings file on disk and write the results next to it.

    Supports JSON, YAML, .strings, Xcode String Catalogs (.xcstrings), .po, ARB,
    Android XML, CSV, XLIFF 1.2, Java/Spring .properties, .NET RESX, Mozilla Fluent
    (.ftl) and Markdown/MDX — every format the API accepts. With only_new=True
    (JSON), translates just the keys missing from existing target files and merges.

    Args:
        file_path: path to the source file, e.g. "locales/en.json".
        target_languages: language codes, e.g. ["de", "uk", "zh"].
        out_dir: where to write outputs (default: the source file's directory).
        only_new: reproducible incremental mode — translate only new AND edited keys, tracked via
                  kaeris.lock (a tone/ICU/glossary change re-translates all); unchanged stays
                  stable. JSON and ARB only — for any other format the whole file is translated
                  (and charged), and the result says so in "note".
        keep: optional list of terms to NEVER translate — kept verbatim in every language
              (brand/product names), e.g. ["KAERIS", "GitHub"].
        context: one line about what the app IS, e.g. "a mobile bank for teenagers" or
              "a wildlife documentary app". The model uses it to pick the right sense of
              ambiguous strings — "Bank" becomes Ufer in the second and Bank in the first.
              You are reading the repo, so you know this: pass it. Max 300 chars.
        verify: back-translate each result into back_lang to confirm meaning (non-incremental only).
        back_lang: language for the verify back-translation (default "en").
        tone: "formal" or "casual" to steer register; "" (default) is neutral.
        icu: True if values may contain ICU MessageFormat (plurals/select) so the
             model preserves the syntax instead of translating it.
        reuse: optional {lang: {key: previous_translation}} translation-memory map —
               strings unchanged from a prior run are reused verbatim server-side
               (non-incremental only; only_new does its own client-side diffing).
        api_key / openrouter_key: optional auth overrides.

    Returns:
        { "written": ["locales/de.json", ...], "languages": [...] }
    

## Claude Desktop Quick Installation
Install path detected from listing signals. Uses `uvx` (confidence: high):

```json
"mcpServers": {
  "kaeris-mcp": {
    "command": "uvx",
    "args": ["kaeris-mcp"]
  }
}
```

## Documentation

## What RaiGanja/kaeris-mcp does

RaiGanja/kaeris-mcp gives MCP-compatible coding assistants tools for inspecting and translating application locale files. It supports 46 target languages and handles common localization formats including JSON, YAML, Flutter ARB, iOS `.strings`, Xcode String Catalogs, gettext PO, Android XML, CSV, XLIFF, Java or Spring properties, .NET RESX, Mozilla Fluent, and Markdown or MDX.

The server separates local inspection from API-backed translation. Repository scanning, locale status checks, and missing-key reports operate on files in the project and do not require an API call. Translation tools send work to the KAERIS service. Language discovery is also available through a free API request that does not count toward usage limits.

## How it works

Start with `kaeris_scan_repo` when the project's locale layout is unknown. It identifies locale files, estimates the source language, lists existing targets, and guesses conventions such as i18next, next-intl, Vue I18n, Flutter, Android, iOS, or gettext. The scan skips common generated and dependency directories and is bounded for larger repositories.

`kaeris_status` compares target locales with a source file. Its results include missing and extra keys, placeholder mismatches, missing files, and quality findings such as number changes, lost inline tags, malformed entities or escapes, and incomplete ICU or CLDR plural categories. `kaeris_list_missing_keys` provides the exact missing or placeholder-broken entries for one locale in pages, including source-language text.

Translation calls preserve placeholders and pass through non-string values. Inline translation can optionally back-translate results for meaning checks and report likely UI overflow. File translation can use `only_new` for JSON and ARB, while `kaeris.lock` records source hashes and translation settings so unchanged entries remain stable across incremental runs.

## Setup and configuration

Install the Python package with:

```bash
pip install kaeris-mcp
```

The process communicates through JSON-RPC on stdin and stdout. It can also run in Docker with the project mounted as `/work`:

```bash
docker build -t kaeris-mcp .
docker run -i --rm -v "$PWD:/work" -w /work kaeris-mcp
```

Claude Desktop can launch `kaeris-mcp` from its MCP configuration, Cursor can add it through MCP settings or `.cursor/mcp.json`, and Claude Code can register it with `claude mcp add kaeris-i18n kaeris-mcp`. No key is needed for the local audit tools or for the free demo translation mode. `KAERIS_API_KEY` supplies a KAERIS key, while `KAERIS_OPENROUTER_KEY` supports the OpenRouter-based option. `KAERIS_API_URL`, `KAERIS_NO_UPDATE_CHECK`, and `KAERIS_NO_CTA` provide additional configuration.

## Tools and capabilities

- `kaeris_list_languages` returns language codes and English names.
- `kaeris_scan_repo` discovers locale files and likely i18n conventions.
- `kaeris_status` checks completeness, placeholders, and translation quality signals.
- `kaeris_list_missing_keys` returns paged missing or broken entries for one target.
- `kaeris_translate` translates key-value strings into one or more languages.
- `kaeris_translate_file` writes translated locale files, with incremental mode for JSON and ARB.
- `kaeris_add_language` creates a new locale by translating the full source file.

## Limitations and notes

Translation operations use the KAERIS API and may be subject to the selected plan's character and language limits. Incremental translation is available only for JSON and ARB; other supported formats are translated as complete files. API keys may also be passed as tool arguments, but environment variables avoid exposing them in conversation transcripts and client logs. Commit `kaeris.lock` when sharing incremental translation state among agents, developers, and CI.

_Full upstream README: https://allmcps.com/mcp/raiganja-kaeris-mcp/readme_

