paraglide-mcp

paraglide-mcp

Enables translation of i18n messages in Paraglide JS projects by serving small batches, validating translations against source structure, and saving them per locale through inlang's SDK.

Category
Visit Server

README

paraglide-mcp

An MCP (Model Context Protocol) server for translating Paraglide JS / inlang projects.

The agent calling the tools is the translator. The server's job is to make that safe and efficient: it serves messages in small batches, validates every translation against the source (placeholders, markup, variant structure) before anything is written, and reports progress so the agent knows exactly when a locale is done. Faulty items are rejected individually — one bad translation never blocks or corrupts the rest of the batch.

Quick start

No installation needed — run it via npx from your MCP client configuration. Add this to your project's .mcp.json (Claude Code) or claude_desktop_config.json:

{
  "mcpServers": {
    "paraglide": {
      "command": "npx",
      "args": ["-y", "paraglide-mcp", "--project", "./project.inlang"]
    }
  }
}

--project is optional: by default the server looks for project.inlang in the working directory, then for a single *.inlang directory up to one level deep.

The server is stateless — it loads your inlang project from disk per tool call and writes changes straight back through your project's own inlang plugin (e.g. @inlang/plugin-message-format), so messages/{locale}.json files always stay in the exact format the rest of your toolchain expects. External edits (your editor, the Paraglide compiler, git) are always picked up. If the configured plugin can't be fetched (offline, no cache), a bundled copy of the message-format plugin is used as a fallback.

Works with the standard Paraglide JS setup out of the box, and with i18next, next-intl, and ICU MessageFormat projects through their inlang plugins — see COMPATIBILITY.md for the full support matrix (and why PO/XLIFF are not supported).

Tools

Tool Purpose
project_info Locales, base locale, and per-locale translated/missing counts.
list_message_keys Keys only (cheap), filterable by key prefix (startsWith) and per-locale status (missing / translated), with cursor pagination.
get_messages Full message content by exact keys or prefix, optionally restricted to specific locales.
search_messages Find messages by text content or key substring (case-insensitive) — for when you know the UI text ("Add to cart") but not the key.
get_translation_batch The next N untranslated messages for a target locale (default 5, max 25), with source text, required placeholders, and a remaining counter.
save_translations Validate and persist translations for one locale (max 25 per call). Per-item results; valid items are saved even when others fail.
delete_messages Delete messages by key from every locale (max 25 per call). Unknown keys are rejected individually while the rest are still deleted.
rename_message Rename a message key across every locale, keeping all translated values. Fails without changes when the old key is missing or the new key is taken.
add_locale Add a locale to the project settings (and seed an empty message file for message-format projects). Locale tags are stored as-is — no format opinion.
remove_locale Remove a locale from the settings and delete its message file. Reports how many translations were discarded; the base locale can't be removed.

Prompts

The server also exposes the common workflows as MCP prompts, so clients that support prompts (e.g. /mcp__paraglide__translate_locale in Claude Code) can launch them directly without the bundled skill:

Prompt Arguments Purpose
translate_locale targetLocale, sourceLocale? Translate all missing messages into one locale via the batch loop.
translate_prefix prefix, targetLocale, sourceLocale? Same loop, scoped to keys starting with prefix.
review_locale locale, prefix? Review existing translations against the base locale and fix problems.

Locale and prefix arguments support MCP completion: locales are suggested from the project settings, prefixes from the actual message keys.

Resources

Read-only project state is also exposed as MCP resources, so clients can pin it as context (e.g. @-mention in Claude Code) without spending tool calls:

Resource Purpose
paraglide://project/info Project overview — same payload as the project_info tool.
paraglide://locales/{locale}/missing All keys missing or empty in {locale}. One resource per project locale appears in the resource list.
paraglide://messages/{locale}/{key} The value of one message in one locale (value is null when untranslated).

All resources return JSON. The {locale} and {key} template variables support MCP completion, like the prompt arguments.

The translation loop

Agents translate iteratively — small batches keep the error rate low while the loop keeps throughput high (no re-reading of the full catalog between steps, and remaining tells the agent exactly when to stop):

project_info
└─ for each target locale:
   ┌─> get_translation_batch { targetLocale: "de", batchSize: 5 }
   │   ... agent translates the 5 items ...
   │   save_translations { targetLocale: "de", translations: [...] }
   └── repeat until done == true

Scope work to a subsection of the catalog with prefix, e.g. only checkout_* messages:

{ "targetLocale": "de", "prefix": "checkout_", "batchSize": 5 }

Message values

Values use the inlang message format — exactly what's in your messages/{locale}.json files:

// simple message
"Hello {name}!"

// multi-variant message (plurals, gender, ...)
[{
  "declarations": ["input count", "local countPlural = count: plural"],
  "selectors": ["countPlural"],
  "match": {
    "countPlural=one": "You have {count} message",
    "countPlural=other": "You have {count} messages"
  }
}]

Translations may change shape when the target language requires it (e.g. a string becomes a plural variant set for Czech) as long as introduced selectors are declared.

Variant arrays with more than one element (found in some legacy or hand-written files) are read in full — placeholders from every element count — but can't be saved back as-is, because the message-format plugin and the Paraglide compiler silently ignore everything after the first element. The save error explains the fix: consolidate all variants into one element's match.

Validation

save_translations rejects, per item:

  • placeholders that don't exist in the source message (typo guard — a {nmae} would otherwise silently become a new input variable),
  • markup tags ({#bold}…) not present in the source,
  • match conditions using undeclared selectors,
  • structurally invalid values,
  • unknown message keys (unless allowNewKeys: true is passed deliberately).

Dropped source placeholders produce warnings, not errors, since languages legitimately drop variables in some variants.

The source-comparison checks can be bypassed per call with skipValidation: true — for translations that deliberately diverge from the source, e.g. when the target doesn't need a placeholder. Structural validation and the unknown-key guard still apply.

Agent skill

skill/paraglide-translation/ contains an installable skill that teaches an agent the batch workflow, plural-rule handling, and error recovery. It uses the open Agent Skills format, so it works with any agent that supports SKILL.md (Claude Code, Codex, Cursor, Copilot, Gemini CLI, ...).

Claude Code — install the plugin, which bundles both the MCP server and the skill (no .mcp.json needed):

/plugin marketplace add WesHaze/paraglide-mcp
/plugin install paraglide-translation@paraglide-mcp

Any other agent — install the skill with the skills CLI (it picks the right directory for your agent), then configure the MCP server as shown in Quick start:

npx skills add WesHaze/paraglide-mcp

Or copy it manually:

cp -r node_modules/paraglide-mcp/skill/paraglide-translation .claude/skills/
# or globally: cp -r ... ~/.claude/skills/

Development

pnpm install
pnpm test        # unit + integration tests (no build needed)
pnpm test:e2e    # builds, then drives the real CLI over stdio MCP
pnpm test:all    # everything
pnpm bench       # performance benchmark (see PERFORMANCE.md)
pnpm build

Integration and e2e tests run against a real inlang project fixture on disk using the actual @inlang/sdk — no mocks.

Message-format projects are read and written directly as JSON instead of through the SDK's load/save cycle — see PERFORMANCE.md for the measurements and rationale.

Releasing

Releases are automated: pushing a v* tag runs release.yml, which tests, publishes to npm via trusted publishing (OIDC — no token secrets), and syncs the version to the official MCP Registry.

One-time setup before the first tagged release:

  1. Publish the first version locally (pnpm build && npm publish) — npm only lets you configure a trusted publisher for a package that already exists.
  2. On npmjs.com → package → Settings, add a GitHub Actions trusted publisher: org WesHaze, repository paraglide-mcp, workflow filename release.yml.
  3. Set publishing access to "Require two-factor authentication and disallow tokens".

Then release with:

npm version patch   # bumps package.json, commits, tags
git push --follow-tags

Requirements

  • Node.js >= 20
  • An inlang project (Paraglide JS default setup works out of the box) — see COMPATIBILITY.md for supported formats and plugins

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured