Skip to main content
Glama
BaconDroid

libretranslate-mcp

by BaconDroid

libretranslate-mcp

build

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.json is committed and CI installs with npm ci, so the dependency tree is pinned and reproducible. Bumping the SDK or zod is 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 / StreamableHTTPServerTransport calls 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.xml still references the placeholder REPLACE_ME/libretranslate-mcp:latest. It must be replaced with a real published image before that template will start; see unraid-stack/docs/translation-mcp.md.

To reproduce the CI checks locally on a machine with Node 20+:

npm ci
npm run typecheck
npm run build

Related MCP server: MCP Server with Local LLM

Requirements

  • Node.js >= 20 (native fetch, AbortSignal.timeout, timingSafeEqual from node: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.json

Run

# 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.js

Environment variables

Variable

Default

Applies to

Meaning

TRANSPORT

stdio

both

stdio or http.

LIBRETRANSLATE_URL

http://localhost:5000

both

Base URL of LibreTranslate. No trailing slash.

LIBRETRANSLATE_TIMEOUT_MS

60000

both

Per-request timeout, via AbortSignal.timeout.

PORT

8787

http

Listen port.

AUTH_TOKEN

(empty)

http

Bearer token required on POST /mcp. Empty means no authentication — a warning is printed to stderr.

MAX_BODY_BYTES

1048576

http

Request body cap; enforced while reading, 413 on exceed.

LOG_LEVEL

info

both

debug / info / warn / error.

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

GET /health

none

Liveness. Never contacts LibreTranslate, so a slow upstream does not make the container look dead.

POST /mcp

bearer, when AUTH_TOKEN is set

Stateless Streamable HTTP. GET/DELETE on this path return 405.

/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

q

string

—

required, non-empty

source

string

"auto"

"auto" asks LibreTranslate to detect

target

string

—

required

format

"text" | "html"

"text"

alternatives

integer 0–10

0

rejected with format: "html"

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

q

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. /translate accepts either a bare JSON string or an object with a translatedText string. Anything else throws an Error containing 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 silent undefined. 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). See src/server.ts.

  • Body cap before buffering. The Content-Length header is checked up front, and the running total is checked on every chunk; on exceed the response is 413 and 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.yml

License

MIT

Available Tools

3 tools
detectDetect 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.

ParametersJSON Schema
NameRequiredDescriptionDefault
qYesA single string, or an array of up to 20 strings, to detect.

TDQS

A4/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.5/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
qYesText to translate.
formatNoInput format: "text" (default) or "html".text
sourceNoSource language code, or "auto" to let LibreTranslate detect it.auto
targetYesTarget language code. Use the languages tool to list the codes this instance supports.
alternativesNoNumber of alternative translations to request (0-10). Only supported with format "text".

TDQS

A4.1/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

  1. 3 tool updatesv0.1.0
    • First observeddetect
    • First observedlanguages
    • First observedtranslate

TDQS

A4.3/5.0

Scored across 3 tools

Disambiguation5/5

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.

Naming Consistency4/5

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.

Tool Count5/5

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.

Completeness5/5

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

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers