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.
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: trueis 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:
- Publish the first version locally (
pnpm build && npm publish) — npm only lets you configure a trusted publisher for a package that already exists. - On npmjs.com → package → Settings, add a GitHub Actions trusted publisher:
org
WesHaze, repositoryparaglide-mcp, workflow filenamerelease.yml. - 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
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
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.
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.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
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.
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.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
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.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.