Skip to content
Ret2HellPublic

About

MCP server for safely inspecting, validating, translating, and updating JSON i18n locale files, with dry-run patches, stale/dead-key detection, and agent workflows

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

i18n-mcp

GitHub Release CI Go MCP Tools Docker Platform License

An MCP server for inspecting, validating, translating, and safely updating JSON i18n locale files in frontend projects. Detect locale layouts, bootstrap .i18n-mcp.json, find missing and stale translations, plan agent translation batches, validate placeholders and ICU shape, preview patches, and write only after explicit approval.

i18n-mcp implements MCP 2026-07-28 over local stdio and stateless Streamable HTTP. Clients must support that protocol revision. It runs with the same filesystem permissions as the user or container process that starts it. Write tools are dry-run by default and require apply: true before changing files.

Security & Trust - This tool reads project files and can write locale JSON, config, and state files when explicitly asked. The default path is preview-first: config writes, translation applies, and key rename return patch previews, while state rebuilds return compact count previews. Nothing is written unless apply: true is provided. The HTTP server binds to loopback by default and refuses non-loopback binds unless authentication is enabled. The built-in static bearer-token verifier is intended only for local development; place production deployments behind a properly authenticated and authorized gateway. Translation proposals are generated by the MCP client or coding agent; the server makes no LLM API requests.

Why i18n-mcp

  • Safe locale writes - every write workflow is dry-run first, returns changed-file metadata and unified diffs, and writes only with apply: true.
  • Framework-aware i18n bootstrap - detects project hints, JSON locale layouts, locales, namespaces, and a proposed .i18n-mcp.json config.
  • Value-aware stale detection - tracks source hashes in .i18n-mcp/state.json instead of storing metadata inside locale files.
  • Translation planning, not blind rewriting - builds deterministic batches for missing and stale keys, then validates proposed translations before apply.
  • Validation built for UI strings - checks placeholders, HTML-like tags, ICU arguments, markdown-sensitive patterns, and empty targets.
  • CI-ready audits - emits Markdown or JSON reports and exits according to configurable failure policy.
  • Flexible MCP transport - use stdio for local coding agents or Streamable HTTP for network-capable MCP clients.

Quick Start

One-line install (macOS / Linux shell):

curl -fsSL https://raw.githubusercontent.com/Ret2Hell/i18n-mcp/main/install.sh | bash

Open OpenCode, Codex, or Claude Code from your frontend project directory and start working. The installer configures supported agents automatically.

Run either transport manually if needed:

# Local stdio
i18n-mcp serve stdio --project /path/to/frontend-app

# Streamable HTTP at http://127.0.0.1:7339/mcp
i18n-mcp serve http --project /path/to/frontend-app

Ask your agent to detect the i18n project. The first MCP tool call is:

{
  "name": "i18n.project.detect",
  "arguments": {}
}

Coding Agent Support

The one-line installer configures these agents by default:

Agent Config written Server project root
OpenCode ~/.config/opencode/opencode.json . from the OpenCode workspace
Codex CLI ~/.codex/config.toml . from the Codex workspace
Claude Code ~/.claude/.mcp.json and ~/.claude.json . from the Claude workspace

After install, restart or open your coding agent from the frontend project directory. The MCP server is registered as i18n-mcp and starts as:

i18n-mcp serve stdio --project .

Use --skip-config if you want to install only the binary and configure agents yourself.

Docker

Pull the image:

docker pull ret2hell/i18n-mcp:latest

Run the stdio server:

docker run --rm -i ret2hell/i18n-mcp:latest serve stdio

For real project access, mount the project directory:

docker run --rm -i -v "$PWD:/workspace" -w /workspace ret2hell/i18n-mcp:latest serve stdio --project /workspace

Run Streamable HTTP on port 7339:

export I18N_MCP_DEV_TOKEN='replace-with-a-random-secret'
docker run --rm -p 127.0.0.1:7339:7339 \
  -e I18N_MCP_DEV_TOKEN \
  -v "$PWD:/workspace" -w /workspace \
  ret2hell/i18n-mcp:latest serve http \
  --project /workspace --addr 0.0.0.0:7339 --auth-required \
  --auth-resource http://127.0.0.1:7339/mcp \
  --dev-static-token-env I18N_MCP_DEV_TOKEN

This is a development-only bearer-token setup. Authentication is required because the process binds to a non-loopback address inside the container. Configure the client to send Authorization: Bearer <token>.

Installation

One-Line Installer

curl -fsSL https://raw.githubusercontent.com/Ret2Hell/i18n-mcp/main/install.sh | bash

Go Run Without Installing

Run directly from the module:

go run github.com/Ret2Hell/i18n-mcp/cmd/i18n-mcp@latest --help
go run github.com/Ret2Hell/i18n-mcp/cmd/i18n-mcp@latest serve stdio --project /path/to/frontend-app

Go Install

go install github.com/Ret2Hell/i18n-mcp/cmd/i18n-mcp@latest

Then run:

i18n-mcp --help
i18n-mcp serve stdio --project /path/to/frontend-app

Build From Source

git clone https://github.com/Ret2Hell/i18n-mcp.git
cd i18n-mcp
go mod download
go test ./...
go build -o bin/i18n-mcp ./cmd/i18n-mcp

Keeping Up to Date

Re-run the installer to replace the binary with the latest release:

curl -fsSL https://raw.githubusercontent.com/Ret2Hell/i18n-mcp/main/install.sh | bash

Uninstall

Remove the installed binary and any project state you no longer want:

rm -f "$(command -v i18n-mcp)"
rm -rf /path/to/frontend-app/.i18n-mcp

Do not remove .i18n-mcp.json unless you also want to remove project configuration.

MCP Client Configuration

The installer configures OpenCode, Codex CLI, and Claude Code automatically using stdio. For other MCP clients, choose either stdio or Streamable HTTP.

{
  "mcpServers": {
    "i18n-mcp": {
      "command": "/absolute/path/to/i18n-mcp",
      "args": ["serve", "stdio", "--project", "/absolute/path/to/frontend-app"]
    }
  }
}

Use absolute paths so the client does not depend on its current working directory.

Streamable HTTP

Start the HTTP server (the default endpoint is http://127.0.0.1:7339/mcp):

i18n-mcp serve http --project /absolute/path/to/frontend-app

Configure a Streamable HTTP-capable client with that URL. The exact configuration keys vary by client; a typical configuration looks like:

{
  "mcpServers": {
    "i18n-mcp": {
      "type": "streamable-http",
      "url": "http://127.0.0.1:7339/mcp"
    }
  }
}

The MCP endpoint is stateless and POST-only; the server separately exposes GET /healthz. Use --addr and --path to change the listen address and MCP endpoint. Loopback HTTP is unauthenticated by default. Browser clients with a different Origin must be explicitly allowed with --trusted-origin https://client.example. See HTTP Deployment before binding to another interface.

Docker-based stdio

For Docker-based clients using stdio, use docker as the command and pass the run arguments explicitly. Ensure the project is mounted into the container.

{
  "mcpServers": {
    "i18n-mcp": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "-v",
        "/absolute/path/to/frontend-app:/workspace",
        "-w",
        "/workspace",
        "ret2hell/i18n-mcp:latest",
        "serve",
        "stdio",
        "--project",
        "/workspace"
      ]
    }
  }
}

First Project Workflow

  1. Call i18n.project.detect.
  2. Review the returned proposedConfig.
  3. Call i18n.config.write with the proposed config and no apply flag to preview .i18n-mcp.json.
  4. Review the patch preview.
  5. Call i18n.config.write again with apply: true to write the config.
  6. Call i18n.config.validate.
  7. Call i18n.state.rebuild without apply to preview state bootstrap.
  8. Call i18n.state.rebuild with apply: true after review.
  9. Call i18n.keys.diff to inspect missing, stale, invalid, extra, and unknown keys.

Features

Project Detection & Config

  • Detect framework hints, i18n libraries, JSON locale file layouts, locales, and namespaces.
  • Generate a proposed .i18n-mcp.json config for review.
  • Validate config through MCP or CLI schema generation.
  • Preview config writes before applying them.

Locale Inventory & Diff

  • List locale files, namespaces, key counts, flattened keys, and duplicate namespace warnings.
  • Compare source and target locales.
  • Report current, missing, stale, invalid, unknown, and extra keys.

Translation Workflow

  • Build deterministic translation batches for missing and stale keys.
  • Include configured style guide and glossary content in plans when requested.
  • Validate proposed translations against current source values.
  • Reject source drift by default.
  • Preview locale JSON and state patches before writing.

Dead-Key Analysis (Temporarily Disabled)

Source usage scanning, dead-key classification, and pruning are not exposed while their safety and scalability are improved. Exact key rename remains available.

CI & Reports

  • Run non-interactive audits with Markdown or JSON output.
  • Configure failure policy for missing, stale, and invalid translations.
  • Generate reports through MCP and read the latest report resource.

MCP Tools

Tool Description
i18n.health Return server version, project root, and health status.
i18n.project.detect Detect project hints, locale layouts, and proposed config.
i18n.config.get Return resolved config with defaults and origin.
i18n.config.validate Validate resolved config.
i18n.config.write Preview or write .i18n-mcp.json; dry-run by default.
i18n.locales.list List locale files, namespaces, key counts, and warnings.
i18n.keys.diff Compare source and target locale keys.
i18n.keys.rename Preview or rename exact locale keys and state; dry-run by default.
i18n.translation.plan Build a translation batch for missing and stale keys.
i18n.translation.validate Validate proposed translations.
i18n.translation.apply Preview or apply translations and update state; dry-run by default.
i18n.state.rebuild Preview or rebuild .i18n-mcp/state.json; dry-run by default.
i18n.report.generate Generate deterministic JSON or Markdown audit reports.

MCP Prompts

Prompt Purpose
i18n_project_bootstrap Guide detection, config creation, and state bootstrap.
i18n_translate_batch Translate a planned batch while preserving validation-sensitive syntax.
i18n_review_translations Review proposed translations before apply.
i18n_ci_report_summary Summarize an audit report for CI or PR review.
i18n_add_feature_keys Plan source keys and translations for a feature.

MCP Resources

Resource Description
i18n://locales Locale inventory summary.
i18n://locales/{locale}/{namespace} Bounded flattened-unit page for one locale namespace; no raw JSON duplication.
i18n://analysis/diff First bounded diff page and complete summary counts.
i18n://translation/plan/latest Latest translation plan.
i18n://reports/latest Latest compact audit summary.
i18n://pages/diff/{query} Diff continuation resource; use the returned URI unchanged.
i18n://pages/locales/{query} Locale-unit continuation resource; use the returned URI unchanged.

Bounded Results and Pagination

MCP is optimized for agent-sized responses, not full project dumps:

Operation Default
i18n.keys.diff 50 records and summary counts for the matching scope
i18n.translation.plan 50 work items; set maxItems to change the batch size
i18n.locales.list Metadata only; includeUnits: true adds a bounded unit page
Locale namespace resource 50 flattened units; raw locale JSON is not embedded
i18n.state.rebuild Counts, assumptions, state path, and warning samples; no full state map
i18n.report.generate Summary, file metadata, warning samples, and a detail-resource link

Diff and locale pages use limit (1–200). Translation batches use maxItems (1–200). Zero/omitted means 50; negative or excessive limits are rejected. Pages have a 64 KiB serialized item budget, so fewer items may be returned even when more were requested. Network diff, locale, planning, report, and resource responses also enforce a 128 KiB result-envelope budget, including escaping and metadata. An individually oversized value or metadata block produces an explicit error rather than silently skipping data; narrow filters or use a local export.

Paged results contain page.returned, page.totalMatched, page.truncated, and page.nextCursor. Diff and locale pages also provide page.nextResource, which can be read directly. Continue until truncated is false. Keep all filters and the requested page size unchanged when passing a cursor. Cursors expire after one hour and are signed, caller/project-bound, and tied to matching result contents; changed results require restarting at page one.

Filter diff analysis with locales, namespaces, keys, keyPrefix, and statuses. Summary counts cover all matching records, not only the returned page. Locale unit pages support locales, namespaces, and keyPrefix. Translation plans retain their existing filters and accept a cursor for subsequent batches. Apply a batch's translations before planning remaining work again; applying changes invalidates affected continuation snapshots.

Example:

{
  "name": "i18n.keys.diff",
  "arguments": {
    "locales": ["fr"],
    "statuses": ["missing", "stale"],
    "limit": 25
  }
}

Structured tool output is canonical in structuredContent; text content is a short pointer rather than a duplicate JSON dump. Markdown reports remain available as text. Warning arrays are sampled to 20 entries with total counts (warningsTotal or inventoryWarningsTotal); report summary warning counts remain complete. Compact reports explicitly set detailsOmitted and provide detailResource.

Expensive MCP operations are admitted one at a time per server; excess requests receive a retryable busy message instead of building an unbounded queue. Health and catalog requests remain available. Cancellation is checked during file traversal, flattening, analysis, state rebuild, and before state writes. Each page recomputes its filtered snapshot; full analysis datasets are not cached. Latest plan/report records are caller-isolated, expire after 15 minutes, and are capped at 64 records per service. Signed batch handles do not depend on those latest-record caches.

See performance measurements for the OteraX Inspector benchmark and remaining limits.

Configuration

i18n-mcp reads .i18n-mcp.json from the project root by default. You can pass a different config path with --config or I18N_MCP_CONFIG.

Generate the JSON Schema:

i18n-mcp schema > i18n-mcp.schema.json

Complete Example

{
  "$schema": "https://example.com/i18n-mcp.schema.json",
  "sourceLocale": "en",
  "targetLocales": ["fr", "de"],
  "localeFiles": ["messages/{locale}/{namespace}.json"],
  "defaultNamespace": "common",
  "translationFunctions": ["t", "i18n.t"],
  "namespaceFunctions": ["useTranslations", "getTranslations"],
  "ignoredKeyPatterns": ["debug.*"],
  "keptKeyPatterns": ["legal.*"],
  "dynamicKeyHints": ["routes.*"],
  "format": {
    "sortKeys": false,
    "indent": 2,
    "trailingNewline": true
  },
  "translation": {
    "styleGuidePath": "docs/i18n-style.md",
    "glossaryPath": "docs/glossary.md"
  },
  "ci": {
    "failOnMissing": true,
    "failOnStale": false,
    "failOnInvalid": true,
    "failOnDeadKeys": false
  }
}

Replace the example $schema URL with your generated schema path or a published schema URL.

Config Fields

Field Required Description
$schema No Optional schema URL for editor validation.
sourceLocale Yes Locale used as the source for translation and source hashes.
targetLocales No Target locales to maintain. Empty means infer from discovered locale files.
localeFiles Yes Project-relative JSON file patterns. Must include {locale} and may include {namespace}.
defaultNamespace No Namespace used when a file pattern has no {namespace} segment.
translationFunctions No Reserved while source usage scanning is disabled.
namespaceFunctions No Reserved while source usage scanning is disabled.
ignoredKeyPatterns No Reserved while dead-key analysis is disabled.
keptKeyPatterns No Reserved while dead-key analysis is disabled.
dynamicKeyHints No Reserved while dead-key analysis is disabled.
format.sortKeys No Sort JSON object keys when writing locale files. Default is false.
format.indent No Number of spaces for JSON indentation. Default is 2.
format.trailingNewline No Write a trailing newline. Default is true.
translation.styleGuidePath No Project-relative style guide file used in translation plans.
translation.glossaryPath No Project-relative glossary file used in translation plans.
ci.failOnMissing No Fail i18n-mcp audit when missing translations exist.
ci.failOnStale No Fail i18n-mcp audit when stale translations exist.
ci.failOnInvalid No Fail i18n-mcp audit when invalid translations exist.
ci.failOnDeadKeys No Retained for compatibility; ignored while dead-key analysis is disabled.

Translation Workflow

Translation generation stays in the MCP client or coding agent. Use i18n.translation.plan, have the agent prepare proposals, call i18n.translation.validate, and preview i18n.translation.apply before applying changes. The server does not make LLM API requests or require API credentials.

Common Locale Layouts

Single JSON file per locale:

{
  "sourceLocale": "en",
  "targetLocales": ["fr", "de"],
  "localeFiles": ["messages/{locale}.json"],
  "defaultNamespace": "common"
}

Namespace files per locale:

{
  "sourceLocale": "en",
  "targetLocales": ["fr", "de"],
  "localeFiles": ["messages/{locale}/{namespace}.json"]
}

Public locales layout:

{
  "sourceLocale": "en",
  "targetLocales": ["fr", "de"],
  "localeFiles": ["public/locales/{locale}/{namespace}.json"]
}

Source directory layout:

{
  "sourceLocale": "en",
  "targetLocales": ["fr", "de"],
  "localeFiles": ["src/locales/{locale}/{namespace}.json"]
}

Validate And Write Config

Validate config through MCP:

{
  "name": "i18n.config.validate",
  "arguments": {}
}

Preview writing config:

{
  "name": "i18n.config.write",
  "arguments": {
    "config": {
      "sourceLocale": "en",
      "targetLocales": ["fr"],
      "localeFiles": ["messages/{locale}.json"],
      "defaultNamespace": "common"
    }
  }
}

i18n.config.write is dry-run by default. Review the returned patch, then call it again with apply: true to write .i18n-mcp.json.

Stale Translation Workflow

Stale translation detection is value-aware. A target translation becomes stale when the source string changes after the target was translated.

State File

i18n-mcp stores translation state in .i18n-mcp/state.json. Locale JSON files are not modified to store metadata.

Each state entry records:

  • Locale.
  • Namespace.
  • Key.
  • Current source hash.
  • Hash the target translation was translated from.
  • Target hash.
  • Status and timestamps.

Bootstrap State

After writing and validating config, rebuild state from existing translations.

Preview first:

{
  "name": "i18n.state.rebuild",
  "arguments": {}
}

Apply after review:

{
  "name": "i18n.state.rebuild",
  "arguments": {"apply": true}
}

Detect Stale Translations

Run the diff tool:

{
  "name": "i18n.keys.diff",
  "arguments": {}
}

Diff statuses include:

  • current: target exists and matches current source state.
  • missing: source key is absent from the target locale.
  • stale: target exists, but the source value hash changed since translation.
  • invalid: target fails placeholder, tag, ICU, or other validation rules.
  • unknown: target exists but state is missing.
  • extra: target key does not exist in the source locale.

Plan Translation Work

Create a batch for missing and stale keys:

{
  "name": "i18n.translation.plan",
  "arguments": {
    "statuses": ["missing", "stale"],
    "includeContext": true
  }
}

Keep the opaque batchId returned in the plan. Pass it back unchanged to validation and apply; it binds the workflow to the project, caller, selected items, and source snapshot without relying on an MCP session. Use the i18n_translate_batch prompt or your agent workflow to generate proposals. Proposals should have this shape:

[
  {
    "locale": "fr",
    "namespace": "common",
    "key": "hello",
    "sourceValue": "Hello",
    "value": "Bonjour"
  }
]

Validate Proposals

{
  "name": "i18n.translation.validate",
  "arguments": {
    "batchId": "<batchId returned by i18n.translation.plan>",
    "translations": [
      {
        "locale": "fr",
        "namespace": "common",
        "key": "hello",
        "sourceValue": "Hello",
        "value": "Bonjour"
      }
    ]
  }
}

Validation rejects source drift by default. If the source string changed after proposals were generated, regenerate the proposal from the latest source value.

Preview Translation Apply

{
  "name": "i18n.translation.apply",
  "arguments": {
    "batchId": "<batchId returned by i18n.translation.plan>",
    "translations": [
      {
        "locale": "fr",
        "namespace": "common",
        "key": "hello",
        "sourceValue": "Hello",
        "value": "Bonjour"
      }
    ]
  }
}

This returns patch previews and does not write files.

Apply Translations After Review

{
  "name": "i18n.translation.apply",
  "arguments": {
    "batchId": "<batchId returned by i18n.translation.plan>",
    "apply": true,
    "translations": [
      {
        "locale": "fr",
        "namespace": "common",
        "key": "hello",
        "sourceValue": "Hello",
        "value": "Bonjour"
      }
    ]
  }
}

Locale files are written first. State is updated only after locale writes succeed.

Stale Translation Troubleshooting

  • unknown means state is missing. Run i18n.state.rebuild after reviewing existing translations.
  • stale means the source text changed. Re-translate from the current source value.
  • invalid means validation failed. Preserve placeholders, tags, ICU arguments, and non-empty target strings.

Dead-Key Analysis Status

i18n.keys.usage_scan, i18n.keys.dead_report, and i18n.keys.prune are temporarily disabled, along with their prompt and analysis resources. The lexical scanner cannot reliably establish that keys are unused, partial scans can produce unsafe prune decisions, and large-project analysis can consume excessive memory.

Implementations and tests are preserved for future work, potentially using AST-based analysis with conservative dynamic-key handling and scan-completeness tracking. They are not registered in production servers.

Reports and CLI audits do not scan application source or classify dead keys. They explicitly warn that this analysis is disabled and omit usage/dead-key results rather than reporting misleading zero counts. ci.failOnDeadKeys is ignored; its config field is retained for compatibility. i18n.keys.rename remains available as an explicit preview-first operation.

CI Usage

i18n-mcp audit runs a non-interactive i18n audit and exits according to the configured CI failure policy.

Run Locally

Markdown output:

i18n-mcp audit --project /path/to/frontend-app --output markdown

JSON output:

i18n-mcp audit --project /path/to/frontend-app --output json

The legacy --refresh-usage flag is deprecated and has no effect while source scanning is disabled.

Audits are compact by default. For a complete local JSON export, use:

i18n-mcp audit --project /path/to/frontend-app --output json --full > i18n-audit.json

Full JSON exports stream diff rows to stdout without retaining the complete diff or caching the export. Check the command's exit status: output can be partial on I/O errors or cancellation, and a complete report can still exit 1 when CI policy fails. Inventory contains file metadata and counts, not duplicate copies of every unit. Full Markdown output is also opt-in with --full, but is rendered in memory; use JSON streaming for large projects.

Failure Policy

Configure failure behavior in .i18n-mcp.json:

{
  "ci": {
    "failOnMissing": true,
    "failOnStale": false,
    "failOnInvalid": true,
    "failOnDeadKeys": false
  }
}

Defaults:

  • Missing translations fail CI.
  • Invalid translations fail CI.
  • Stale translations are reported but do not fail CI.
  • Dead-key analysis is disabled; failOnDeadKeys has no effect.

Exit Codes

  • 0: audit completed and no configured failure condition matched.
  • 1: runtime error or configured failure condition matched.

Failure diagnostics are written to stderr. Report content is written to stdout.

GitHub Actions Example

name: i18n-audit

on:
  pull_request:
  push:
    branches: [main]

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: "1.27.3"
      - run: go build -o bin/i18n-mcp ./cmd/i18n-mcp
      - run: bin/i18n-mcp audit --project . --output markdown > i18n-audit.md
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: i18n-audit
          path: i18n-audit.md

MCP Report Tool

Generate a report through MCP:

{
  "name": "i18n.report.generate",
  "arguments": {
    "format": "markdown"
  }
}

Read the latest generated report:

i18n://reports/latest

CLI Reference

i18n-mcp --help
i18n-mcp serve stdio --project /path/to/frontend-app
i18n-mcp serve stdio --project /path/to/frontend-app --config .i18n-mcp.json --log-level warn
i18n-mcp serve http --project /path/to/frontend-app
i18n-mcp serve http --project /path/to/frontend-app --addr 127.0.0.1:7339 --path /mcp
i18n-mcp serve http --project /path/to/frontend-app --trusted-origin https://inspector.example
i18n-mcp schema > i18n-mcp.schema.json
i18n-mcp audit --project /path/to/frontend-app --output markdown
i18n-mcp audit --project /path/to/frontend-app --output json

JSON Processing

Application code uses Go's standard-library JSON v2 directly on every architecture, without Sonic or a JSON wrapper. It rejects duplicate keys, invalid UTF-8, and trailing input; configuration field names are case-sensitive. See the JSON v2 migration notes for output changes, dependency boundaries, and upgrade considerations.

Security Model

i18n-mcp supports stdio and Streamable HTTP. With either transport, the server has the same filesystem access as the user running it.

Project Root Guard

All project file access is constrained to the configured project root.

The root guard rejects:

  • Path traversal outside the project root.
  • Absolute paths outside the project root.
  • Symlink escapes for guarded reads and writes.
  • Writes through symlink path components.

Dry-Run Defaults

Write tools default to preview mode.

Tool Writes Files Default
i18n.config.write .i18n-mcp.json Dry-run
i18n.state.rebuild .i18n-mcp/state.json Dry-run
i18n.translation.apply Locale JSON and state Dry-run
i18n.keys.rename Locale JSON and state Dry-run

Actual writes require explicit apply: true.

Patch Preview

Write tools return structured changed-file output and unified diffs before writing. Review patches before applying.

Atomic Writes

Locale, config, and state writes use temporary files plus atomic rename where possible. If a multi-file operation partially fails, the tool reports which files were written and which were not.

Destructive Operations

Rename moves locale keys and is destructive. Automated pruning is temporarily disabled.

Safety rules:

  • Rename rejects destination conflicts by default.
  • Overwrite behavior must be explicit.

Credentials

Do not store credentials in .i18n-mcp.json, .i18n-mcp/state.json, reports, prompts, or tool arguments.

The server does not send translation content to external LLM APIs. Translation plans and requested style-guide or glossary context are returned to the MCP client or coding agent; that client's data-handling policies apply.

Reports

Reports include i18n-relevant config, locale summaries, and translation diff status. Source usage and dead-key analysis are temporarily disabled. Reports must not include unrelated source files, environment variables, or credentials.

Stdio Deployment

Stdio is intended for a single user running the server against a project checkout.

Recommendations:

  • Run the server only for trusted projects.
  • Use a project-specific root with --project.
  • Review dry-run patches before apply.
  • Do not expose stdio server processes to untrusted clients.

HTTP Deployment

Streamable HTTP defaults to 127.0.0.1:7339, serves MCP at /mcp, and exposes a /healthz endpoint. /mcp implements the stateless MCP 2026-07-28 transport: each JSON-RPC message is a separate POST, no initialize handshake or Mcp-Session-Id is used, and GET/DELETE session operations and SSE resumption are not available. Resource changes are delivered through a client-opened subscriptions/listen POST stream. The server refuses to bind to a non-loopback address unless --auth-required is set.

The built-in bearer-token mode provides protected-resource metadata and scope checks, but its static token verifier is for development only. Clients must send the token as Authorization: Bearer <token>:

export I18N_MCP_DEV_TOKEN='replace-with-a-random-secret'
i18n-mcp serve http \
  --project /path/to/frontend-app \
  --auth-required \
  --auth-resource http://127.0.0.1:7339/mcp \
  --dev-static-token-env I18N_MCP_DEV_TOKEN

Requests with a browser Origin are checked for cross-origin access. Same-origin requests are accepted; allow each intentional browser client explicitly, for example --trusted-origin https://inspector.example. Native MCP clients that do not send an Origin continue to work.

MRTR confirmations and translation batch handles are signed. The server generates an ephemeral signing key by default. Set I18N_MCP_REQUEST_STATE_SECRET to the same random value of at least 32 bytes on every replica when handles must survive process replacement or move between replicas. Never commit or log this value.

For production or remote access, keep the server on loopback or a private interface behind a gateway that provides TLS, token validation, authorization, rate limiting, and secret-safe audit logging. Do not expose project files over a network without authentication and authorization.

Troubleshooting

Problem Fix
MCP client cannot find the server For stdio, use an absolute command path and restart the client. For HTTP, verify the client supports Streamable HTTP and can reach the configured /mcp URL.
HTTP server rejects the listen address Non-loopback binds require --auth-required; use loopback for local clients or configure authentication.
Server starts in the wrong project Pass --project /absolute/path/to/frontend-app.
Config validation fails Run i18n.project.detect, review proposedConfig, then preview i18n.config.write.
Translation batch handle is stale or invalid Run i18n.translation.plan again and pass its new batchId unchanged to validate/apply.
Diff shows unknown keys Run i18n.state.rebuild without apply, review, then run with apply: true.
Stale translations remain Re-plan with i18n.translation.plan and regenerate proposals from current source values.
Dead-key tools are missing Usage scanning, dead-key classification, and pruning are temporarily disabled pending safer analysis.
Audit fails CI Check .i18n-mcp.json ci policy and inspect the Markdown or JSON report.
Docker cannot see project files Mount the project with -v /absolute/project:/workspace and pass --project /workspace.
Binary not found after install Add the install directory to PATH or install with --dir "$HOME/bin".

Distribution & Release Maintenance

This project uses GoReleaser for cross-platform binaries, checksums, SBOMs, GitHub Releases, and Docker images.

Release workflow outputs include:

  • Linux, macOS, and Windows archives.
  • SHASUMS256.txt checksums.
  • SBOM artifacts.
  • Docker image tags.

Create a release with:

DOCKER_IMAGE=ret2hell/i18n-mcp goreleaser release --clean

Docker image tags:

ret2hell/i18n-mcp:VERSION
ret2hell/i18n-mcp:latest

License

See LICENSE.

About

MCP server for safely inspecting, validating, translating, and updating JSON i18n locale files, with dry-run patches, stale/dead-key detection, and agent workflows

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages