libretranslate-mcp
Provides tools for translating text, detecting language, and listing supported languages through a self-hosted LibreTranslate instance.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@libretranslate-mcpTranslate 'thank you very much' from English to German."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
libretranslate-mcp
An MCP server that exposes a self-hosted LibreTranslate instance as three
tools: translate, detect, languages.
It is a thin, honest wrapper over LibreTranslate's REST API. It does no translation itself, has no glossary, no document translation, and no translation memory.
What is verified, and what is not
The badge above is the source of truth for the current CI state. This section deliberately makes no claim about it, because such claims go stale on every run. What follows are durable facts, none of which a CI run can change.
Not verified — runtime behaviour
The client has never been pointed at a real LibreTranslate instance. The response shapes it parses are implemented from a reading of LibreTranslate's
app.py, not from observed traffic. That is why the parsing is defensive: a payload mismatch is designed to fail loudly, but it has never actually mismatched in the wild.The server has never been started against a real upstream. Neither transport has been observed working, no request has ever been served, and the bearer auth, the body cap and the 401/405/413 paths have only been reasoned about, never exercised.
Dependency surface
package-lock.jsonis committed and CI installs withnpm ci, so the dependency tree is pinned and reproducible. Bumping the SDK orzodis now a deliberate, reviewable change to the lockfile rather than something that happens silently on the next run.The pinned versions were resolved once and never re-checked against the SDK's published type definitions by hand. The
McpServer/registerTool/StreamableHTTPServerTransportcalls follow the SDK's documented usage; a real signature mismatch would surface as a typecheck failure, not a silent breakage.
Not verified — published image
unraid-stack/my-libretranslate-mcp.xmlstill references the placeholderREPLACE_ME/libretranslate-mcp:latest. It must be replaced with a real published image before that template will start; seeunraid-stack/docs/translation-mcp.md.
To reproduce the CI checks locally on a machine with Node 20+:
npm ci
npm run typecheck
npm run buildRelated MCP server: MCP Server with Local LLM
Requirements
Node.js >= 20 (native
fetch,AbortSignal.timeout,timingSafeEqualfromnode:crypto)A reachable LibreTranslate instance
A LibreTranslate instance that has the language pairs you intend to use loaded — see "Which languages are available" below
Install and build
npm install
npm run build # tsc -p tsconfig.jsonRun
# stdio (default) — for local MCP clients that spawn the server
node dist/index.js
# http — for a container
TRANSPORT=http PORT=8787 AUTH_TOKEN=$(openssl rand -hex 32) node dist/index.jsEnvironment variables
Variable | Default | Applies to | Meaning |
|
| both |
|
|
| both | Base URL of LibreTranslate. No trailing slash. |
|
| both | Per-request timeout, via |
|
| http | Listen port. |
| (empty) | http | Bearer token required on |
|
| http | Request body cap; enforced while reading, |
|
| both |
|
All logs go to stderr, always. stdout carries the stdio JSON-RPC framing and any stray write there corrupts the protocol.
HTTP endpoints
Route | Auth | Notes |
| none | Liveness. Never contacts LibreTranslate, so a slow upstream does not make the container look dead. |
| bearer, when | Stateless Streamable HTTP. |
/health is unauthenticated by design: a liveness probe that fails without a
token is a liveness probe nobody runs. It exposes no secrets — service name,
version, transport mode, whether auth is on, the upstream URL, and the body cap.
Clients of POST /mcp must send Accept: application/json, text/event-stream,
which is what the MCP Streamable HTTP transport specifies. That is why
curl alone is a poor smoke test; use the MCP inspector (see
../unraid-stack/docs/translation-mcp.md).
Tools
translate
Input | Type | Default | Notes |
| string | — | required, non-empty |
| string |
|
|
| string | — | required |
|
|
| |
| integer 0–10 |
| rejected with |
Returns translatedText, plus detectedLanguage when the upstream response
carries one. With alternatives > 0, the candidates are included when the
response has them; when the field is absent, the result says so explicitly
instead of returning an empty list that looks like "no alternatives existed".
format: "html" with alternatives > 0 is refused before any network
call: LibreTranslate does not return alternatives for HTML input, and sending
it produces a confusing upstream error instead of a clear one.
detect
Input | Type | Notes |
| string or string[] (1–20) | one detection per input, in order |
languages
No input. Lists what this instance can do: code, name, and targets per
language. Use it to pick valid source/target values — a language absent
here cannot be used.
Which languages are available
This client does not know. LibreTranslate only loads the language models the
deployment asks for (LT_LOAD_ONLY on the official container). A target
value that is not loaded produces an upstream 400, which is surfaced verbatim
in the tool error. Call languages to see reality.
Design notes
Defensive response parsing.
/translateaccepts either a bare JSON string or an object with atranslatedTextstring. Anything else throws anErrorcontaining the received shape (JSON-stringified, truncated to ~300 chars) and the endpoint, so a payload change is diagnosable from the log instead of surfacing as a silentundefined. The same posture applies to/detect(array or single object) and/languages(array of objects).Non-2xx responses always carry the status, the endpoint, and the upstream
{"error": "..."}body when there is one.createServer()is a factory, not a singleton. Registering tools on a module-level server instance makes it impossible to build a second one in the same process, which the stateless HTTP transport needs (one transport + one server per request). Seesrc/server.ts.Body cap before buffering. The
Content-Lengthheader is checked up front, and the running total is checked on every chunk; on exceed the response is413and the request stream is destroyed. The body is never accumulated in full and measured afterwards.Token comparison is constant-time.
crypto.timingSafeEqual, with a length check first because that function throws on unequal buffer lengths.
Project layout
src/
index.ts entry point, TRANSPORT switch, HTTP transport
(auth, body cap, per-request transport)
server.ts createServer() factory
constants.ts shared constants
types.ts shared types
services/
libretranslate.ts HTTP client + defensive response parsing
tools/
translate.ts registerTranslateTools
detect.ts registerDetectTools
languages.ts registerLanguagesTools
tsconfig.json
Dockerfile
entrypoint-mcp.sh
.env.example
.github/workflows/build.ymlLicense
MIT
Available Tools
3 toolsdetectDetect languageA
Detect the language of one or more strings using a self-hosted LibreTranslate instance (POST /detect). Returns one detection per input string, in input order, with the confidence LibreTranslate reported when it provided one.
| Name | Required | Description | Default |
|---|---|---|---|
| q | Yes | A single string, or an array of up to 20 strings, to detect. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It discloses the HTTP method (POST), the input shape (one or more strings), the output ordering (input order), and the confidence field (when provided). This is substantial behavioral context, though it omits error handling, authentication requirements, and rate limits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the core purpose, and each clause adds meaningful information (endpoint, input cardinality, output order, confidence). No redundancy or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter tool with no output schema, the description covers the essential return contract (per-input ordering, confidence). It doesn't discuss failure modes or edge cases (e.g., unsupported languages, empty array), but those are minor given the simplicity. Overall adequate for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the parameter 'q' is fully documented with its type and constraints. The description reiterates 'one or more strings' but adds no new semantic detail (e.g., accepted language codes, format hints, or detection heuristics). It meets the baseline without extra value.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool detects the language of one or more strings, names the specific endpoint (POST /detect), and describes the return behavior (one detection per input, in order, with confidence). This distinguishes it from siblings like 'translate' and 'languages' without needing to open their schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The purpose is clear but there is no explicit guidance on when to use this tool versus the siblings. It doesn't say 'use this to detect language before translating' or 'use languages to list supported codes.' The usage is implied by the verb and resource, but no alternatives or exclusions are mentioned.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
languagesList supported languagesA
List the languages a self-hosted LibreTranslate instance can work with (GET /languages). Each entry has a code, a name, and the list of target codes that language can be translated into on this instance. The list reflects the instance configuration, not this client: a language missing here cannot be used as a target.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it does so well. It discloses the endpoint, the structure of each entry (code, name, target codes), and the important behavioral fact that the list reflects the instance configuration rather than the client. It does not discuss errors or auth, but for a simple read-only list this is adequate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each earning its place. The first states the action and scope, the second describes the return data, and the third provides the critical configuration-dependent nuance. No filler or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a parameterless list operation with no output schema, the description is complete. It tells an agent what the tool does, what each result contains, and how to interpret missing entries. Nothing essential is missing for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the baseline is 4. The description adds useful detail about the result entries and the meaning of target codes, which compensates for any ambiguity in an empty input schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('List') and resource ('languages'), and clarifies the scope to a self-hosted LibreTranslate instance. It also implicitly distinguishes itself from the sibling tools translate and detect by being the only listing operation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear context for when this is useful: to determine which languages the instance supports and which target codes are valid. It implies that a language missing from this list cannot be used as a target, but it does not explicitly mention alternatives or when not to use this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
translateTranslate textA
Translate text using a self-hosted LibreTranslate instance (POST /translate). Set source to "auto" to let LibreTranslate detect the input language; the detected language and its confidence are then reported in the result. format "html" treats the input as HTML; LibreTranslate does not support alternatives for HTML input, so format "html" with alternatives > 0 is rejected before the request is sent. With format "text" and alternatives > 0, the other candidate translations returned by LibreTranslate are included when the response carries them, and an explicit note is returned when it does not.
| Name | Required | Description | Default |
|---|---|---|---|
| q | Yes | Text to translate. | |
| format | No | Input format: "text" (default) or "html". | text |
| source | No | Source language code, or "auto" to let LibreTranslate detect it. | auto |
| target | Yes | Target language code. Use the languages tool to list the codes this instance supports. | |
| alternatives | No | Number of alternative translations to request (0-10). Only supported with format "text". |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses specific behavioral traits: auto-detection reports the detected language and confidence, format 'html' with alternatives > 0 is rejected before the request, and with 'text' alternatives are included only when the response carries them, with an explicit note otherwise. This goes beyond schema details and covers important edge cases, though it does not address error handling or authentication.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single dense paragraph with no filler. Every sentence adds relevant operational detail (auto-detection, format restrictions, alternatives behavior). It is front-loaded with the core purpose and then covers edge cases efficiently. Slightly long but each sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 5 parameters, no output schema, and no annotations, the description covers the main behaviors needed for correct invocation: auto-detection, html limitations, and alternatives handling. It does not explicitly describe the return format (e.g., that it returns translated text), but that is implied. It adequately covers the tool's complexity, though it could mention response structure or error handling for full completeness.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds meaningful semantics beyond the schema: it explains the behavior of 'source=auto' (detection and reporting), the rejection of html with alternatives, and the conditional inclusion of alternative translations. This adds value over the schema's parameter descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Translate') and a specific resource ('text using a self-hosted LibreTranslate instance'), clearly distinguishing it from sibling tools like 'detect' (language detection) and 'languages' (listing codes). It is unambiguous about what the tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives useful usage context (e.g., setting source to 'auto', the format restrictions, and how alternatives behave) but does not explicitly guide the agent on when to choose this tool over its siblings (e.g., 'use detect for language detection' or 'use languages to list codes'). The guidance is implicit rather than explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.1.0- First observed
detect - First observed
languages - First observed
translate
TDQS
Scored across 3 tools
Each tool maps to a distinct LibreTranslate API endpoint: languages lists supported languages, translate performs translation, and detect identifies language. There is no overlap, and agents can easily select the right tool based on the operation needed.
All tool names are simple, lowercase, single words that clearly indicate their function. While 'languages' is a noun and 'translate'/'detect' are verbs, the naming style is consistent in length and simplicity, and there is no confusion or mixed conventions.
With exactly three tools, the surface is minimal but perfectly scoped for the core functionality of a translation API. Each tool is essential, and the count falls well within the typical range for a well-scoped server.
The tools cover the primary operations of the LibreTranslate API: listing languages, translating text, and detecting language. There are no obvious missing features for a basic translation service, and agents can perform standard workflows (e.g., detect source, then translate) without dead ends.
Maintenance
Related MCP Connectors
LibreTranslate MCP — open-source machine translation (BYO endpoint)
Translate MCP — LibreTranslate-compatible machine translation.
MCP server for Crowdin — projects, files, strings, translation progress, tasks, and members.
MCP server for Translation Services
Related MCP Servers
- AlicenseAqualityDmaintenanceAn MCP server providing machine translation capabilities via the Lara Translate API, featuring language detection and context-aware translations between numerous language pairs.22170 npm97MIT
- FlicenseNot gradedqualityDmaintenanceIntegrates local language models (like Qwen3-8B) with MCP clients, providing tools for chat, code analysis, text generation, translation, and content summarization using your own hardware.-
- AlicenseNot gradedqualityCmaintenanceWraps LibreTranslate API to enable translation capabilities for AI agents via MCP.2 npmMIT
- AlicenseBqualityDmaintenanceAn MCP server that wraps the Tolgee localization platform API, enabling LLMs to manage translation projects, keys, translations, languages, and related workflows.27MIT