# @hoyasumii/libretranslate > An unofficial TypeScript SDK for the LibreTranslate API, with an MCP server and a CLI built on top of it. This file contains all documentation content in a single document following the llmstxt.org standard. ## Getting started `@hoyasumii/libretranslate` is a TypeScript SDK for the [LibreTranslate](https://libretranslate.com) HTTP API, with an MCP server and a CLI built on top of it. Use it from code, from an AI agent, or from your terminal: all three share the same client. - **SDK**: typed methods for translating texts and lists, detecting languages, translating files and sending suggestions. It is generated by [orval](https://orval.dev) from an OpenAPI spec written for this package, and sends your API key when the instance needs one. Start at [SDK](./sdk/overview.md). - **MCP server** (`@hoyasumii/libretranslate/mcp`, bin `libretranslate-mcp`): stdio or Streamable HTTP on `127.0.0.1`. Tools to translate texts and documents, detect languages and check the instance, plus generic tools for the raw API. Start at [MCP server](./mcp/overview.md). - **CLI** (`libretranslate`): every MCP tool as a subcommand, plus `libretranslate mcp` to configure the server, run it in the background, start it at login and register it in Claude Code, Codex and OpenCode. Start at [CLI](./cli/overview.md). This is an independent, **unofficial** client, MIT licensed. It talks to a LibreTranslate instance over HTTP and contains no code from the LibreTranslate project. ### A LibreTranslate instance You need an instance to talk to: - **Your own**, with Docker: `docker run -p 5000:5000 libretranslate/libretranslate` serves `http://localhost:5000`, the URL this package uses by default. It needs no API key. - **With this CLI**, when Docker is installed: `libretranslate service up --languages en,pt,es` does the same and saves the URL (see [`libretranslate service`](./cli/service.md)). - **A hosted one**, such as [libretranslate.com](https://libretranslate.com), which requires an API key. ### Installation Requires Node.js 20 or later. ```bash npm i -g @hoyasumii/libretranslate # or: pnpm add -g @hoyasumii/libretranslate libretranslate mcp config # the instance URL and, if it issues keys, an API key; saved per user libretranslate mcp install # registers the server (stdio) in Claude Code / Codex / OpenCode ``` As a library, `npm install @hoyasumii/libretranslate`. ### A first call ```ts import { createLibreTranslateClient } from "@hoyasumii/libretranslate"; const lt = createLibreTranslateClient({ baseUrl: "http://localhost:5000" }); const { translatedText, detectedLanguage } = await lt.translate({ q: "Olá, mundo!", target: "en" }); // "Hello, world!", { language: "pt", confidence: 90 } ``` From the terminal, once `libretranslate mcp config` has saved a configuration: ```bash libretranslate translate --q "Olá, mundo!" --target en libretranslate languages --source pt ``` `libretranslate docs` opens this site. --- ## Errors | Class | When | | ---------------------------- | ------------------------------------------------- | | `LibreTranslateApiError` | A non-2xx response | | `LibreTranslateConfigError` | Invalid configuration: a missing or malformed URL | | `LibreTranslateTimeoutError` | No response within `timeoutMs` | `LibreTranslateApiError` carries `status`, `method`, `path` (without the query string) and the response `body`. LibreTranslate answers errors as `{ "error": "" }`, and that message is the error's. | Status | Usually | | ------ | ------------------------------------------------------------------------- | | 400 | A missing or invalid parameter, an unsupported file, or a key is required | | 403 | The API key is invalid, or the client is banned | | 429 | Too many requests: wait and retry | | 500 | The translation failed on the instance | ```ts import { LibreTranslateApiError } from "@hoyasumii/libretranslate"; try { await lt.translate({ q: "Olá", target: "en" }); } catch (error) { if (error instanceof LibreTranslateApiError && error.status === 429) { // back off and retry } else throw error; } ``` ### The API key never shows Every error's message and `body` go through `redact`: the values of keys such as `api_key` become `***`, and so does any literal occurrence of the client's own key, inside strings too. `redact` is exported for your own logs: ```ts import { redact } from "@hoyasumii/libretranslate"; console.log(redact(payload, [process.env.LIBRETRANSLATE_API_KEY!])); ``` --- ## Files An instance can translate whole documents: plain text, office documents, subtitles, e-books and more. The formats it accepts are in `settings().supportedFilesFormat` (e.g. `.txt`, `.docx`, `.pptx`, `.odt`, `.epub`, `.srt`, `.pdf`), and `settings().filesTranslation` says whether it is enabled at all. ### Upload and download ```ts import { readFile, writeFile } from "node:fs/promises"; const data = await readFile("report.docx"); const { translatedFileUrl } = await lt.translateFile({ file: new Blob([data]), filename: "report.docx", source: "en", target: "pt", }); const translated = await lt.downloadFile(translatedFileUrl); await writeFile(translated.filename, translated.data); ``` - `file` is a `Blob` or a `File`. A `File` brings its own name; a `Blob` needs `filename`, because the instance picks the format by the extension. - `source` defaults to `auto`. - `translateFile` answers the URL the instance serves the result from. `downloadFile` fetches it as `{ data: Uint8Array, filename, contentType }`, `filename` coming from the server's `Content-Disposition`. An unsupported extension or a disabled feature answers 400 (`LibreTranslateApiError`). From an AI agent, `libretranslate_translate_file` does all of this from a local path and saves the result beside it (see [Tools](../mcp/tools.md)). --- ## Overview ## SDK ```ts import { createLibreTranslateClient } from "@hoyasumii/libretranslate"; const lt = createLibreTranslateClient({ baseUrl: "https://libretranslate.example.com", apiKey: process.env.LIBRETRANSLATE_API_KEY, // only for instances that issue keys }); const { translatedText } = await lt.translate({ q: "Bom dia", source: "pt", target: "en" }); const languages = await lt.languages(); const settings = await lt.settings(); ``` `baseUrl` is the instance URL, with its base path if it has one (`https://example.com/translate`). `createLibreTranslateClientFromEnv()` reads `LIBRETRANSLATE_URL` (default `http://localhost:5000`) and `LIBRETRANSLATE_API_KEY`. ### Options | Option | What for | | ----------- | ----------------------------------------------------------------------------- | | `baseUrl` | The instance URL (required) | | `apiKey` | The API key, for instances that issue keys; sent in the body of every request | | `timeoutMs` | Timeout per request, default 60000; `0` or `Infinity` turn it off | | `fetch` | An alternative `fetch` (tests, a proxy) | Every method also takes a last `{ signal }` argument, to cancel the call with an `AbortSignal`. ### Methods | Method | What for | | ----------------------------------------------------------- | --------------------------------------------------------------- | | `translate({ q, source?, target, format?, alternatives? })` | One text ([Translation](./translation.md)) | | `translateMany({ q: string[], … })` | Several texts in one request | | `detect(text)` | The candidate languages, most likely first | | `languages()` | Every source language, with the codes it translates into | | `translateFile({ file, filename?, source?, target })` | Upload a document ([Files](./files.md)) | | `downloadFile(url)` | Download a translated file as bytes | | `suggest({ q, s, source, target })` | Send a better translation back (when the instance accepts them) | | `settings()` | Key required, character limit, file formats, suggestions | | `health()` | `{ status: "ok" }` when the instance is up | | `call(operationId, body?)` | Any operation by its `operationId`, with the raw body | A non-2xx answer becomes a `LibreTranslateApiError` (see [Errors](./errors.md)). ### How it is generated The package keeps its own OpenAPI 3.1 description of the API in `spec/openapi.yml`, written from the public API documentation. [orval](https://orval.dev) turns it into: - `src/generated/endpoints.ts`: one function per operation, all sending through the package's own `fetch` wrapper, which adds the base URL, the timeout and the error handling; - `src/generated/model/`: the request and response types (`TranslateRequest`, `Detection`, `FrontendSettings`, …), exported from the package; - `src/generated/zod.ts`: zod schemas of every request, which the MCP tools use as their inputs. The client above wraps those functions with defaults (`source: "auto"`) and the API key. For every exported type and function, see the [API reference](pathname://../../docs/api). --- ## Translation ### One text ```ts const result = await lt.translate({ q: "Olá, mundo!", target: "en" }); result.translatedText; // "Hello, world!" result.detectedLanguage; // { language: "pt", confidence: 90 } ``` `source` defaults to `auto`: the instance detects the language and says which it found in `detectedLanguage`. Give `source` when you know it, which is faster and avoids a wrong guess on short texts. ### Several texts `translateMany` sends a list in one request and answers lists in the same order: ```ts const { translatedText } = await lt.translateMany({ q: ["Bom dia", "Boa noite"], source: "pt", target: "es" }); // ["Buenos días", "Buenas noches"] ``` With `source: "auto"`, `detectedLanguage` is a list too, one per text. ### HTML `format: "html"` keeps the markup and translates only the text inside it: ```ts await lt.translate({ q: '

Olá!

', source: "pt", target: "en", format: "html" }); // '

Hello!

' ``` ### Alternatives `alternatives: n` asks for up to `n` other translations besides the main one: ```ts const { translatedText, alternatives } = await lt.translate({ q: "Olá", source: "pt", target: "it", alternatives: 2 }); // "Ciao", ["Salve", "Pronto"] ``` ### Detection ```ts const candidates = await lt.detect("Bonjour tout le monde"); // [{ language: "fr", confidence: 92 }, …] ``` ### Language codes `languages()` lists every source language with the codes it translates into. Use those codes as `source` and `target` (they include regional ones, such as `pt-BR` or `zh-Hant`). ```ts const languages = await lt.languages(); const portuguese = languages.find((language) => language.code === "pt"); portuguese?.targets; // ["en", "es", …] ``` ### Limits `settings()` tells what the instance allows: - `charLimit`: the most characters one request may carry (`-1` is no limit). Split longer texts, for instance by paragraph with `translateMany`. - `keyRequired`: whether an API key is needed. Without one, the instance answers 400. - `suggestions`: whether `suggest` is accepted. ### Suggestions ```ts await lt.suggest({ q: "Olá", s: "Hi", source: "pt", target: "en" }); ``` Sends a better translation back to the instance, which keeps it. Only instances with suggestions enabled accept it. --- ## Configuration Each setting comes from a flag, then the environment, then the file `libretranslate mcp config` saved, then the default. | Variable | What for | Default | | ------------------------ | ------------------------------------------------------------ | ----------------------- | | `LIBRETRANSLATE_URL` | The instance URL | `http://localhost:5000` | | `LIBRETRANSLATE_API_KEY` | The API key, for instances that issue keys | none | | `PORT` | The HTTP port | `3768` | | `LIBRETRANSLATE_CONFIG` | Where the saved configuration lives (also `--config`) | see below | | `LIBRETRANSLATE_MCP_URL` | For the CLI: a running `libretranslate-mcp` to use (`--url`) | in-process server | A self-hosted instance usually needs no key. A hosted one, such as libretranslate.com, does: without it every translation answers 400. `libretranslate status` tells which. ### Where it is saved - `~/.config/libretranslate/.env` on Linux; - `~/Library/Application Support/libretranslate/.env` on macOS; - `%APPDATA%\libretranslate\.env` on Windows; - or wherever `LIBRETRANSLATE_CONFIG`/`--config` points. The file is written with mode 0600 (on Windows the folder's ACL protects it). The API key never shows up in errors, logs, `--help` or the configuration form. --- ## Generic tools The curated tools cover what an agent usually needs. Three more reach the raw API, operation by operation, as the spec describes it: 1. **`libretranslate_resources`** lists the operations (`translate`, `detect`, `listLanguages`, `getFrontendSettings`, `suggest`, `health`). With a `query`, only the matching ones. 2. **`libretranslate_describe`** gives one operation's method, path, body schema and an example `libretranslate_call` input. With `schema`, it expands one schema from the spec (`depth` levels deep, default 3). 3. **`libretranslate_call`** runs it: `operation` is the `operationId` (`listLanguages`) or `METHOD /path` (`GET /languages`), and `body` is the JSON request body. ```json { "operation": "translate", "body": { "q": "Olá", "source": "pt", "target": "en", "alternatives": 2 } } ``` ### Safety - Unknown body fields are refused, so a typo does not silently drop a setting. - `api_key` is refused: the server sends the configured key itself, and the schemas leave it out. - Operations that write to the instance (`suggest`) need `confirm: true`. The agent is told to ask you first. - File upload is left out of the catalog: `libretranslate_translate_file` covers it from a local path. - A long answer is cut at 60,000 characters, saying so. The catalog is generated from the same OpenAPI spec as the SDK (`pnpm codegen:mcp`). --- ## Overview(Mcp) ## MCP server `libretranslate-mcp` gives Claude Code, Codex, OpenCode or any other MCP client machine translation through a LibreTranslate instance. ```bash libretranslate-mcp # stdio: what the MCP client runs libretranslate-mcp --http # http://127.0.0.1:3768/mcp (PORT changes the port) libretranslate mcp start # the same in the background; stop/status; boot enable to start it at login ``` ### Tools | Tool | What for | | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------ | | `libretranslate_translate` | One text or a list, with `auto` detection, HTML and alternatives | | `libretranslate_translate_file` | A local document, saved beside the original as `.` | | `libretranslate_detect` | The candidate languages of a text | | `libretranslate_languages` | The language codes, or the targets of one source | | `libretranslate_status` | Health, whether a key is required, the character limit, the file formats | | `libretranslate_suggest` | Sends a better translation back, when the user asks for it | | `libretranslate_resources` / `libretranslate_describe` / `libretranslate_call` | The raw API, operation by operation | - [Tools](./tools.md): the curated tools in detail. - [Generic tools](./generic-tools.md): the raw API. ### The HTTP mode - It is stateless and listens on `127.0.0.1` only. - It refuses a `Host` that is not loopback, against DNS rebinding. - `GET /health` answers `{ ok, baseUrl, apiKey, version }`, `apiKey` saying whether one is configured. - `POST /shutdown` with the token `libretranslate mcp start` generates closes it; that is how `libretranslate mcp stop` stops the server, Windows included. In stdio mode, stdout carries the protocol: the server logs to stderr only. Errors never carry the API key: every tool error goes through the same masking as the SDK's. --- ## Programmatic use `@hoyasumii/libretranslate/mcp` exports the server and its transports. ### Stdio ```ts import { serveLibreTranslateMcpStdio } from "@hoyasumii/libretranslate/mcp"; const mcp = await serveLibreTranslateMcpStdio({ baseUrl: "http://localhost:5000", apiKey: process.env.LIBRETRANSLATE_API_KEY, // optional }); await mcp.closed; // settles when the client closes stdin ``` stdout carries the protocol, so nothing else may write to it. `stdin`/`stdout` can be other streams. ### HTTP ```ts import { startLibreTranslateMcpServer } from "@hoyasumii/libretranslate/mcp"; const server = await startLibreTranslateMcpServer({ port: 0, baseUrl: "http://localhost:5000" }); console.log(server.url); // http://127.0.0.1:/mcp await server.close(); ``` `port: 0` picks a free port. `shutdownToken` enables `POST /shutdown` (the request carries it in `X-LibreTranslate-Shutdown`), and `onShutdown` runs after it closed the server. ### Any transport ```ts import { createLibreTranslateClient } from "@hoyasumii/libretranslate"; import { buildLibreTranslateMcpServer } from "@hoyasumii/libretranslate/mcp"; const server = buildLibreTranslateMcpServer(createLibreTranslateClient({ baseUrl, apiKey })); await server.connect(transport); ``` `buildLibreTranslateMcpServer` answers the bare `McpServer`, with every tool registered and no transport attached. ### Also exported - `resolveMcpConfig`, `clientFor`, `configFilePath`, `readEnvFile`, `writeEnvFile`: the configuration the CLI uses. - `CATALOG`, `searchCatalog`, `describeOperation`, `invoke`: what the generic tools run on. --- ## Setup ### The quick way Save your settings once, then let the CLI register the server in the clients it finds: ```bash npx libretranslate mcp config # asks for the instance URL, an API key if it issues keys, and a port npx libretranslate mcp install # finds Claude Code, Codex and OpenCode on your PATH and registers libretranslate-mcp (stdio) ``` `install` shows a checklist of the clients it found. Tick the ones you want, and it registers the server through each client's own CLI, under the name `libretranslate`. The registered command reads the saved configuration when the client launches it, so no API key ends up in the client's config. See [`libretranslate mcp install`](../cli/mcp-commands.md#libretranslate-mcp-install) for the flags. ### By hand: stdio Let the client start `libretranslate-mcp`. It reads the saved configuration, so the client config needs no keys: ```json { "mcpServers": { "libretranslate": { "command": "npx", "args": ["-y", "-p", "@hoyasumii/libretranslate", "libretranslate-mcp"] } } } ``` In Claude Code: ```bash claude mcp add libretranslate -- npx -y -p @hoyasumii/libretranslate libretranslate-mcp ``` Without a saved configuration, the server uses `http://localhost:5000` and no key. To point it elsewhere, or to override the saved file, give the client an `env` block (see [Configuration](./configuration.md)): ```json { "mcpServers": { "libretranslate": { "command": "npx", "args": ["-y", "-p", "@hoyasumii/libretranslate", "libretranslate-mcp"], "env": { "LIBRETRANSLATE_URL": "https://libretranslate.com", "LIBRETRANSLATE_API_KEY": "your-api-key" } } } } ``` ### By hand: HTTP Run one server in the background and point your clients at its URL: ```bash npx libretranslate mcp start # prints the URL, http://127.0.0.1:3768/mcp by default claude mcp add --transport http libretranslate http://127.0.0.1:3768/mcp ``` Without the CLI, `libretranslate-mcp --http` runs it in the foreground with the settings from the environment or the saved configuration. `libretranslate-mcp --help` lists the flags. To start the server at every login, run `npx libretranslate mcp boot enable` (see [`libretranslate mcp boot`](../cli/mcp-commands.md#libretranslate-mcp-boot)). ### Checking it works Ask your agent to call `libretranslate_status`, or run it from the terminal: ```bash npx libretranslate status ``` It answers the instance URL, whether a key is configured and required, the character limit and the file formats. --- ## Tools Their inputs are the zod schemas orval generates from the spec, so the agent sees the same fields, enums and defaults as the SDK. ### `libretranslate_translate` Translates one text, or a list in one call. | Input | What for | | -------------- | --------------------------------------------------------- | | `q` | The text, or a list of texts | | `target` | The target language code | | `source` | The source language code; `auto` (the default) detects it | | `format` | `text` (default) or `html`, which keeps the markup | | `alternatives` | How many other translations to add (default 0) | It answers the API's own shape: `translatedText`, plus `detectedLanguage` with `auto` and `alternatives` when asked. ### `libretranslate_translate_file` Translates a local document and saves the result. | Input | What for | | ----------- | -------------------------------------------------------------------------- | | `path` | The file to translate | | `target` | The target language code | | `source` | The source language code, `auto` by default | | `output` | Where to save the translation (default: beside the original) | | `overwrite` | Replace an existing file (default false: an existing one is never touched) | `report.docx` translated into `pt` becomes `report.pt.docx`. It answers `{ savedTo, bytes, translatedFileUrl }`. The server reads and writes files on the machine it runs on, with your user's permissions. ### `libretranslate_detect` The candidate languages of `q`, most likely first, each with a confidence from 0 to 100. ### `libretranslate_languages` Without input, the codes and names of every language. When every language translates into every other (the usual case), the targets are listed once instead of once per language. With `source`, the languages that one translates into. ### `libretranslate_status` The instance's health and settings in one answer: whether an API key is required and whether one is configured, the character limit per request, whether file translation and suggestions are enabled, and the accepted file formats. A good first call for an agent. ### `libretranslate_suggest` Sends a corrected translation (`s`) of a text (`q`) back to the instance, which keeps it. The agent is told to use it only when you ask, and it fails on instances with suggestions disabled. ### Errors the agent can act on A tool error is text that says what to do: an instance that requires a key answers with a hint to save one with `libretranslate mcp config`, a 403 points at the configured key, a 429 says to wait. The key itself never appears. --- ## libretranslate mcp ## `libretranslate mcp` `libretranslate mcp` manages the MCP server for you: its saved configuration, a background HTTP server, a login service, and its registration in your MCP clients. ```bash npx libretranslate mcp config # asks for the settings in the terminal and saves them npx libretranslate mcp config --base-url http://localhost:5000 --port 4000 # no prompts (scripts, CI): saves just these npx libretranslate mcp config --web # the same, in a local web form npx libretranslate mcp install # pick Claude Code / Codex / OpenCode and register libretranslate-mcp (stdio) in them npx libretranslate mcp install --client claude,opencode --force # no picker (scripts, CI); --force replaces an entry npx libretranslate mcp uninstall # pick the clients to remove the 'libretranslate' entry from (no saved config needed) npx libretranslate mcp start # start in the background (needs a saved config); prints the URL for `claude mcp add` npx libretranslate mcp start --api-key other --port 4000 # one-off values, never saved npx libretranslate mcp status # running or stopped (exit 3), URL, pid, uptime npx libretranslate mcp stop npx libretranslate mcp boot enable # start at every login; `boot disable` / `boot status` ``` ### `libretranslate mcp config` Writes the saved `.env` ([Configuration](../mcp/configuration.md)). It works three ways: - **In the terminal** (the default). It asks for each setting in turn, starting from the saved values (`http://localhost:5000` for a new URL). The API key is typed masked: enter keeps the saved one (or leaves none), and `-` clears it. - **With flags.** Given any of `--base-url`, `--api-key` (`-` clears it) or `--port`, it asks nothing and saves just those. Without a terminal, it needs them. A key passed as a flag stays in your shell history, so prefer the prompt for it. - **In a web form** with `--web`: a local page, opened in the browser (`--no-open` to only print its URL). A blank secret keeps the saved one. If a server is running, it says so: restart it to pick the changes up. `--config ` (or `LIBRETRANSLATE_CONFIG`) writes another file. ### `libretranslate mcp install` Detects each client by running its `--version`, and registers the stdio server through the client's own CLI, under the name `libretranslate`: | Client | Command it runs | | ----------- | --------------------------- | | Claude Code | `claude mcp add -s user` | | Codex | `codex mcp add` | | OpenCode | `opencode mcp add --global` | The registered command is `node /dist/mcp/cli.js` by absolute path, with no credential: the server reads the saved file when the client launches it (`LIBRETRANSLATE_CONFIG` is passed only when `--config` names another file). Every client found starts ticked. One that already has a `libretranslate` entry is marked `already installed, reinstalls` and gets it replaced. Without an interactive terminal, `--client` is required (`claude`, `codex`, `opencode`; inside WSL also `claude@windows`, `codex@windows`, `opencode@windows`), plus `--force` to replace an entry. `--dry-run` prints the commands instead of running them. Both `install` and `uninstall` work on each client's user-level (global) config. Project-scoped entries are never touched. ### `libretranslate mcp uninstall` Lists the clients with a `libretranslate` entry, showing whether it is `stdio` or `http`, and removes any entry of that name: `claude mcp remove -s user`, `codex mcp remove`, and for OpenCode (which has no `remove`) an edit of its global config file that deletes only that key, keeping comments and layout. It is the one command besides `libretranslate mcp config` that runs without a saved configuration, so a client can be cleaned up after the configuration is gone. `--client` and `--dry-run` work as in `install`. ### `libretranslate mcp start`, `stop` and `status` `start` runs the HTTP server detached, with its pid and log in `/run/`, and prints its URL, its log file and the `claude mcp add` line to register it. It needs a saved configuration. `--api-key`, `--base-url` and `--port` override it for this run only, and are never saved. `--foreground` serves in the current process instead. `status` prints whether the server is running, with its URL, pid and uptime, and exits with code 3 when it is not. `stop` asks the server to shut down through a token-guarded `POST /shutdown`, and signals the process only if that fails. ### `libretranslate mcp boot` `boot enable` installs a service of the current user that starts the server at every login, so no sudo is needed: | OS | Service | | ------- | --------------------------------------------------------------- | | Linux | a systemd user unit (on WSL, enable systemd in `/etc/wsl.conf`) | | macOS | a LaunchAgent | | Windows | a logon task | The service reads only the saved configuration. `boot disable` removes it and `boot status` reports it. --- ## CLI overview ## CLI The package installs a `libretranslate` command. It is an MCP client of the [same server](../mcp/overview.md): every MCP tool becomes a subcommand, and the tool's input schema becomes its flags. By default the server runs inside the command, so there is nothing to start first. ```bash npx libretranslate mcp config # once: the instance URL and, if it issues keys, an API key npx libretranslate tools # every command, one per MCP tool npx libretranslate status npx libretranslate translate --q "Olá, mundo!" --target en npx libretranslate translate --q '["Bom dia","Boa noite"]' --source pt --target es npx libretranslate translate --q "Olá" --target en --format html --alternatives 2 npx libretranslate translate-file --path report.docx --target pt npx libretranslate detect --q "Bonjour tout le monde" npx libretranslate languages --source pt npx libretranslate call --operation getFrontendSettings ``` Nothing but `--help`, `--version`, `libretranslate docs`, `libretranslate mcp config` and `libretranslate mcp uninstall` runs until a configuration with a URL is saved. `libretranslate docs` prints the link to this site and opens it in the browser. ### From tools to commands - The command is the tool's name without `libretranslate_`, in kebab-case: `libretranslate_translate_file` → `translate-file`. - Each flag is an input in kebab-case. - Array flags take `a,b` or JSON, object flags take JSON, and boolean flags need no value. - `libretranslate --help` lists a command's flags, with the allowed values of enum inputs. Tool output goes to stdout. A tool error goes to stderr with exit code 1. ### One-off settings and a running server `--base-url` and `--api-key` override the environment and the saved file for one run of the in-process server. To use a `libretranslate-mcp` that is already running over HTTP instead, pass `--url http://127.0.0.1:3768/mcp` or set `LIBRETRANSLATE_MCP_URL`. These flags work anywhere on the command line. None of them stands in for the saved configuration: the CLI refuses to run tools without it, even when `--url` or `--base-url` is given. ### A local instance With Docker installed, `libretranslate service up` runs LibreTranslate in a container and points the CLI at it. See [`libretranslate service`](./service.md). ### Managing the server `libretranslate mcp` is intercepted before any connection is made. It configures the server, runs it in the background, starts it at login and registers it in your MCP clients. See [`libretranslate mcp`](./mcp-commands.md). --- ## libretranslate service ## `libretranslate service` `libretranslate service` runs a LibreTranslate instance on your machine, in a Docker container, so the SDK, the MCP server and the CLI have something to talk to. It needs [Docker](https://docs.docker.com/get-docker/). Without it, the command does not even show in `libretranslate --help`, and every subcommand first checks that Docker is installed and that its daemon answers, saying what to do when not. ```bash npx libretranslate service up --languages en,pt,es # create and start it, wait until it answers npx libretranslate service status # container, image, URL, health (exit 3 when it does not answer) npx libretranslate service logs --follow # what it is doing (the first start downloads models) npx libretranslate service down # stop it; --remove also deletes the container ``` Unlike the tool commands, it needs no saved configuration: it is how a local instance gets started in the first place. ### `up` The first time, it pulls the image (with its progress), then creates the container: - named `libretranslate`, published on `127.0.0.1` only (port 5000, or `--port`); - with the language models in the `libretranslate-models` volume, so they are downloaded once; - with only the `--languages` given loaded, which makes it start much faster (default: every language); - from `libretranslate/libretranslate:latest`, or `--image`. Then it waits for `/health` to answer (up to 15 minutes, `--timeout `; `--no-wait` returns at once), and saves `http://localhost:` as the CLI's instance when none is saved yet. When another instance is saved, it keeps it and prints the `libretranslate mcp config --base-url …` line to switch. When the container already exists, `up` only starts it: `--port`, `--languages` and `--image` apply when it is created, so it says so. To change them, `libretranslate service down --remove` first. The instance it runs issues no API keys, so nothing else is needed. ### `down` Stops the container. `--remove` also deletes it; the models stay in the volume (`docker volume rm libretranslate-models` deletes them). ### `status` and `logs` `status` prints the container's state, image, URL and whether `/health` answers, and exits with code 3 when it does not. `logs` prints the last 100 lines (`--tail `), or keeps printing new ones with `--follow` (`-f`). --- ## Windows and WSL The package runs on Windows 10/11 with Node.js 20 or later, from PowerShell or cmd. - The settings file lives in `%APPDATA%\libretranslate\.env`, protected by that folder's per-user permissions (file modes mean nothing on Windows). - `libretranslate mcp boot enable` registers a logon task. - `libretranslate mcp stop` asks the server to shut down through a token-guarded `POST /shutdown` before falling back to terminating it: a signal on Windows is `TerminateProcess`, which would not let the server shut down. - The client CLIs are run through `cross-spawn`, so Windows `.cmd` shims work. - Files are written through a rename that retries, because antivirus and editors hold files open. ### From WSL When the package is installed inside WSL, `libretranslate mcp install` and `uninstall` also list the clients installed on the Windows side, as `Claude Code (Windows)` and so on (`--client claude@windows`). They start the server with `wsl.exe -d -e node …/dist/mcp/cli.js`, so it keeps reading the configuration saved inside WSL. The first call after WSL has been idle waits for the distro to start (a second or two). The Windows side is reached through `powershell.exe`, taken from the PATH or, with `appendWindowsPath = false`, from `/mnt/c/Windows/System32/WindowsPowerShell/v1.0/`. When it cannot be reached, `--client claude@windows` says which step failed. To start the server at login inside WSL, enable systemd in `/etc/wsl.conf` first, then run `npx libretranslate mcp boot enable`. --- ## Contributing The repository is [Hoyasumii/libretranslate](https://github.com/Hoyasumii/libretranslate), managed with pnpm (Node.js 20 or later). ```bash pnpm install # dependencies, plus the git hooks (husky) pnpm build # tsc → dist/ (CommonJS + .d.ts) pnpm test:unit # jest, with a fake LibreTranslate on node:http (no network) pnpm test:live # against a real instance (.env.test, from env.example) pnpm check:types # tsc --noEmit over src, tests and scripts pnpm check:lint # oxlint (`pnpm fix:lint` fixes what it can) pnpm check:format # oxfmt, 120 columns (`pnpm fix:format` rewrites) pnpm check:knip # unused files, exports and dependencies ``` Every script is cross-platform: no `rm`, `$VAR` or `VAR=1 cmd`. `.gitattributes` keeps LF, with `.cmd`/`.vbs` in CRLF. The checks run locally through git hooks. `pre-commit` runs `check:lint` and `check:format`, `commit-msg` runs commitlint with the conventional config (`feat: …`, `fix(mcp): …`), and `pre-push` runs `check:types`, `check:knip` and `test:unit`. Every push to `main` runs the Continuous Delivery workflow (`.github/workflows/cd.yml`). It runs the same checks and the build, then: - publishes `package.json`'s version to npm when that version is not on the registry yet (through Trusted Publishing, with provenance), tags it `v` and opens a GitHub release; - builds the site and deploys it to the `gh-pages` branch when the push touches `website/` or `src/` (a manual run of the workflow always deploys it). To release, bump `version` in `package.json` and merge to `main`. ### Generated code ```bash pnpm codegen # spec/openapi.yml → src/generated/ (orval) and src/mcp/generated/catalog.json pnpm codegen:mcp # only the MCP catalog (a unit test fails when it is stale) ``` `spec/openapi.yml` is the single source: an OpenAPI 3.1 description of the LibreTranslate API written for this package from the public API documentation and the answers of a running instance. `orval.config.ts` generates the `fetch` functions (through `src/transport.ts`), the model types and the zod schemas. Never edit `src/generated/` by hand: change the spec and run `pnpm codegen`. To follow a change in the API: update the spec, run `pnpm codegen`, adjust `src/client.ts` and the tools if a method changes, and check it against a real instance with `pnpm test:live`. This package is MIT and stays independent: it describes the API from its documentation and observable behavior, and never copies code from the LibreTranslate project or its MCP server (both AGPL-3.0). ### This site The site is a Docusaurus workspace package in `website/`, in English and Portuguese (Brazil). ```bash pnpm docs:dev # preview (append `--locale pt-BR` for the translation) pnpm docs:build # build every locale into website/build/ pnpm docs:serve # serve the build (search only works on a build) GIT_USER= pnpm docs:deploy # build and push to the gh-pages branch ``` - The guides are plain Markdown in `website/docs/`, mirrored page for page in `website/i18n/pt-BR/docusaurus-plugin-content-docs/current/`: change both together. - The [API reference](pathname://../docs/api) is generated from `src/index.ts` and `src/mcp/index.ts` by TypeDoc on every English build. - `llms.txt` and `llms-full.txt` are generated at the site's root from the English guides on every build.