The agent tool surface
An agent can translate through BetterTranslator over the Model Context Protocol. It is the same translation the window makes, through the same factory and the same guards, and the results land in the same chat history.
What it is
The implementation is the C# package ModelContextProtocol 2.1.0. The server identifies itself as bettertranslator
and carries instructions the agent reads on connect. An agent translation is routed by content, JSON first, then Markdown, then prose, and is sent a unit at a time rather than as one all or nothing generation. The stored temperature and the standing instruction from Advanced travel with it, the same verification runs afterwards, and the reassembly's findings arrive as a note.
Two transports
Both register the identical ten tools through the same code path.
- A pipe the agent starts, through
bt mcp - An HTTP endpoint hosted inside the application, mapped at
/mcp
There is one behavioural difference. A separately launched pipe server has no window to talk to, so the tool that reveals an entry in the interface reports that there is no window: show_in_gui
The ten tools
A test asserts that the served surface is exactly these ten and nothing else.
| Tool | What it does |
|---|---|
| translate_text | Synchronous. Takes the text and a source and target language, and answers with the result, the model, a token count, a duration, any note and the identifier of the stored entry. |
| translate_file | Asynchronous. Answers with a job identifier and a queued state, and errors before queueing when the file does not exist or the format has no reader. |
| translate_batch | Asynchronous. Answers with a job identifier, the folder and the file count. Only the files directly inside the folder are taken. |
| job_status | The polling tool for both asynchronous jobs. Answers with the state, the progress, and a result row per file. Idempotent. |
| job_cancel | Files already written stay written and the rest are not translated. Calling it on a finished job changes nothing. |
| list_languages | No parameters. Answers with the model and a row per language carrying the code, the name, the endonym, the script, the direction, the availability and the reason. |
| list_models | No parameters. Answers with the name, file, path, size, installed flag and selected flag of each model. |
| select_model | Matches by name, file name or full path. The choice is stored and outlives the server, and a running window picks it up at once. |
| show_in_gui | The one tool that needs the window. Without one it returns a tool error saying so. |
| get_entry | Reads one stored translation by identifier. There is no tool that lists entries. Source and result are clipped, and the clip says how many characters there were in all. |
Memory is an option on the text tool alone, and it is off unless asked for. It does not exist on the file or folder tools: use_memory
Every tool carries a description, an input schema whose every property is described, an output schema and annotations, so an agent needs no documentation beyond the protocol's own tool listing.
Job identifiers and polling
Identifiers count up from job_0001
One job runs at a time, so a queued job waits for the running one. A job finishes as failed when it produced no results or when every result failed, and as done otherwise. The states are: queued running done failed cancelled
What every tool answers with
Each answer carries a machine readable structure in snake case alongside a human readable Markdown block. An error sets the error flag with a plain sentence and no structure. Tables are real Markdown tables, and every cell is clipped so one long value cannot flood the transcript.
Turning it on
The endpoint is off by default. When enabled it binds loopback: 127.0.0.1:8765
Settings has an Agent section holding the toggle, a status line, the address, the token and two copyable registration commands. The listener stops and resumes without restarting the application.
Registering it
Over HTTP, with the application running:
claude mcp add --transport http bettertranslator http://127.0.0.1:8765/mcp
When a token is set, append the authorization header: --header "Authorization: Bearer <token>"
Over a pipe, with no application running:
claude mcp add -s user bettertranslator -- "<path to>\cli\bt.exe" mcp
The full path is required, because a bare name resolves only where it is on the executable search path, and an agent that cannot find it reports a closed connection rather than a missing file. A build puts a copy beside the application executable. For a client configured by a JSON file, the entry is a standard input and output server whose command is that full path with a single argument.
Refusals and the network
Startup refuses a port outside the valid range and an empty host. It also refuses any non loopback bind that has no token, on the ground that binding a public address opens the machine to the network. The name localhost counts as loopback.
With a token set, the authorization header is compared exactly and anything else is rejected.
Two limits belong in the same breath as those refusals. When a non loopback bind is deliberately configured the traffic is plain HTTP, because the host has no encrypted branch at all. And a file job from an agent is not restricted to any particular folder.
Diagnosing a dead connection
The first line on standard error is a banner:
bettertranslator mcp <version>, built <yyyy-MM-dd HH:mm>, from <path>
A build time older than your last change means the agent is running a stale copy that has no mcp command. Rebuild, then reconnect.
Where the results go
An agent session gets a chat of its own, named after the first thing in it and then renamed by the model that just translated, with an entry per translation. An open window picks the row up without a restart.
Only the text tool returns an entry identifier, and that is what the entry reader and the reveal tool consume: entry_id