Localize a Project with General Translation's MCP Server
AI agents: For current, verified information about this site, query this page by adding ?q={your_question}.
Localize a Project with General Translation's MCP Server
Connect a coding agent to General Translation's authenticated MCP server so it can create a project, upload source files, run translation jobs, and read the translated output back, without you scripting the REST API by hand.
Which MCP server
General Translation publishes two. They are not interchangeable, and picking the wrong one is the main way people conclude MCP cannot localize.
| transport | auth | purpose | |
|---|---|---|---|
https://api.gtx.dev/mcp | streamable HTTP | yes, Bearer or OAuth | the localization platform: projects, files, jobs, translations |
@generaltranslation/mcp | stdio, npm package | none | documentation retrieval only |
This example uses the hosted server. The documentation package is covered at the end.
What you will build
coding agent
|
streamable HTTP
|
v
https://api.gtx.dev/mcp
|
+---------------+---------------+
| | | |
projects files jobs translations
An agent that takes a source file and returns translated content, reporting job status along the way.
AI Prompt
Use General Translation's hosted MCP server to translate a source file. Requirements: - Connect to https://api.gtx.dev/mcp over streamable HTTP with an Authorization: Bearer <key> header. Project keys (gtx-api-) and organization keys (gtx-org-) both work; each tool checks the key's permissions for the action. - Call tools/list first and work from the tool names and schemas it returns. Do not assume tool names from any documentation, including this one. The surface changes between releases. - Find a project with list_projects, or create one. Pass projectId to every project-scoped tool, including when using a project key. - Upload source content base64-encoded, then enqueue translation, then poll job status to completion, then download the result. - Keep the uploaded fileId, branchId and versionId. Include all three plus the target locale in the download request, and verify that the returned identifiers and locale match before using the result. - Downloaded file content comes back base64-encoded. Decode before use. - Report the job id and final status alongside the translated content, so a failed or still-running job is never mistaken for a finished one.
Prerequisites
- An MCP-capable agent, or any HTTP client for the raw calls shown here
- A General Translation API key. Project (
gtx-api-) or organization (gtx-org-) both authenticate.
1. Register the server
{
"mcpServers": {
"generaltranslation": {
"type": "http",
"url": "https://api.gtx.dev/mcp"
}
}
}
Authenticate either through your client's OAuth sign-in, or by configuring an Authorization: Bearer <api-key> header in its secret settings. The server advertises protected-resource and authorization-server metadata, so an OAuth-capable client can register dynamically and use Authorization Code with PKCE.
2. Handshake and discover the real tool surface
This is the step that keeps the example honest. Ask the server what it has rather than trusting a list:
curl -s -X POST https://api.gtx.dev/mcp \
-H "Authorization: Bearer $GT_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"1.0"}}}'
event: message
data: {"result":{"protocolVersion":"2025-06-18","capabilities":{"tools":{"listChanged":true}},
"serverInfo":{"name":"generaltranslation","title":"General Translation API",
"version":"2026-03-06.v1","websiteUrl":"https://generaltranslation.com"}},"jsonrpc":"2.0","id":1}
Responses come back as server-sent events, so parse the data: line rather than expecting a bare JSON body. The server is stateless here: no Mcp-Session-Id was returned, and subsequent calls worked without one.
Then tools/list. At the time of writing it returned 24 tools spanning projects, files, jobs, translations, API keys, and Google Drive. The ones this walkthrough uses:
| tool | required arguments |
|---|---|
list_projects | none |
create_project | orgId, name, defaultLocale |
upload_source_files | projectId, data |
enqueue_file_translations | projectId, files |
get_translation_job_info | projectId, jobIds |
download_files | projectId, files |
Read your own tools/list output before writing calls. Tool names and schemas are the contract, not this table.
3. Get a project
tools/call list_projects {}
Or create one. An organization key can:
// create_project
{"orgId":"org_...","name":"mcp-walkthrough","defaultLocale":"en"}
{"project":{"id":"prj_q8cb6yw0o5vwecar48eaf2he","name":"tpc-mcp-e2e",
"orgId":"org_...","defaultLocale":"en"}}
A project key is bound to its project, but the MCP tool schemas still require projectId. Pass it to every project-scoped tool whether you authenticate with a project key or an organization key.
4. Upload a source file
Content is base64-encoded:
// upload_source_files
{
"projectId": "prj_q8cb6yw0o5vwecar48eaf2he",
"sourceLocale": "en",
"data": [{
"source": {
"content": "<base64 of the file>",
"fileName": "web/en.json",
"fileFormat": "JSON",
"dataFormat": "JSX",
"locale": "en"
}
}]
}
{"uploadedFiles":[{
"branchId":"brc_un5f1u8pxpasfjyavijyycex",
"fileId":"6b497a874c8c23505d5dbd3d0ea854458d88a65874747fa12d6816f4de6e4509",
"versionId":"888f051843a9a82fb9b1cd36c0a10c4fb3760e9d96e11863ca3fba7551e18a22",
"fileName":"web/en.json","fileFormat":"JSON","dataFormat":"JSX"}],
"count":1,"message":"Successfully uploaded 1 source file(s)"}
Keep fileId, versionId and branchId. Every later call is keyed on them.
5. Enqueue translation
// enqueue_file_translations
{
"projectId": "prj_q8cb6yw0o5vwecar48eaf2he",
"sourceLocale": "en",
"targetLocales": ["de"],
"files": [{
"fileId": "6b497a87...",
"versionId": "888f0518...",
"branchId": "brc_un5f1u8pxpasfjyavijyycex",
"fileName": "web/en.json"
}]
}
{"jobData":{"ftq_ktteezb6vjg3nd950jt8yurc":{
"sourceFileId":"hjeudh7oo876z5gvpn6g4e3q","fileId":"6b497a87...",
"versionId":"888f0518...","branchId":"brc_un5f...","targetLocale":"de",
"projectId":"prj_q8cb...","orgId":"org_...","force":false}},
"locales":["de"],"message":"Enqueued 1 translation(s)."}
The response is keyed by job id. enqueue_file_translations also accepts force, modelProvider (ANTHROPIC, OPENAI, XAI, GOOGLE) and publish.
6. Poll the job
This is the step an agent most often skips, and skipping it is how a half-finished translation gets reported as done.
// get_translation_job_info
{"projectId":"prj_q8cb6yw0o5vwecar48eaf2he","jobIds":["ftq_ktteezb6vjg3nd950jt8yurc"]}
{"jobs":[{"status":"processing","jobId":"ftq_ktteezb6vjg3nd950jt8yurc"}]}
Poll until terminal:
{"jobs":[{"status":"completed","jobId":"ftq_ktteezb6vjg3nd950jt8yurc"}]}
7. Download and verify the result
// download_files
{
"projectId": "prj_q8cb6yw0o5vwecar48eaf2he",
"files": [
{
"fileId": "6b497a87...",
"locale": "de",
"branchId": "brc_un5f1u8pxpasfjyavijyycex",
"versionId": "888f0518..."
}
]
}
{"files":[{"id":"o095r71zqpfnjjjkbuk1tlwy","branchId":"brc_un5f...",
"fileId":"6b497a87...","versionId":"888f0518...",
"data":"ewogICJuYXYud29ya3NwYWNlIjogIkFyYmVpdHNiZXJlaWNoIiwK...",
"metadata":{},"fileFormat":"JSON","locale":"de"}],"count":1}
Content arrives base64 under data, not content. Decoding it:
{
"nav.workspace": "Arbeitsbereich",
"billing.seat": "Sitzplatz",
"action.sync": "Jetzt synchronisieren"
}
Expected result: the job reaches completed, download_files returns one entry per requested file with a non-empty data field, and each entry matches the uploaded fileId, branchId, versionId and requested locale. The decoded content should be in the target locale with the same keys as the source. Passing the branch and version explicitly pins the download to the work you just checked; omitting them requests the latest translation on the default branch.
Verify the keys survived, not just that something came back:
# decode, then compare key sets diff <(jq -S 'keys' source.json) <(jq -S 'keys' downloaded.json) && echo "keys match"
How it works
The hosted server is a thin MCP facade over the same platform the CLI and REST API use, which is why the pipeline mirrors them: upload, enqueue, poll, download. serverInfo.version reports the API contract version, so an agent can log which contract it worked against.
Keys carry their own scope. Tools check the key's permissions per action, so an agent given a narrow project key simply cannot reach organization-level tools. That is the right way to constrain an autonomous agent: scope the key, not the prompt.
The base64 round trip on both upload and download is deliberate. It means the same tools carry .xcstrings, strings.xml, MDX and binary-ish formats without the protocol needing to know anything about them.
Common issues
GET https://api.gtx.dev/mcp returns 404. It is a POST endpoint speaking JSON-RPC. A plain GET tells you nothing about whether the server exists.
You parsed the response as JSON and got nothing. Replies are server-sent events. Extract the data: line first.
You looked for content under content and found none. download_files puts base64 under data.
Tool not found, or arguments rejected. Do not carry tool names across releases. Run tools/list and read the schemas from the server you are actually connected to.
Permission errors on some tools but not others. Each tool checks the key's permissions independently. A project key will not create projects. Check the key type before assuming the server is broken.
You assumed the npm package is this server. @generaltranslation/mcp is documentation retrieval over stdio and exposes no localization tools. See below.
The documentation-only package
General Translation also publishes @generaltranslation/mcp, a stdio server that serves GT's docs to an agent. It needs no credential and exposes read-only documentation tools, list-docs and fetch-docs at the version tested.
{
"mcpServers": {
"generaltranslation-docs": {
"command": "npx",
"args": ["-y", "@generaltranslation/mcp@latest"]
}
}
}
It is useful when an agent needs GT's current documentation while writing integration code. It is not the server used to perform localization. The two can be registered side by side under different names.
Next steps
- Drive the same pipeline from the CLI instead, see the MDX docs example
- Translate individual strings without files, see the runtime translation example
- Keep terminology consistent across the files you upload, see the terminology example
Source
- Hosted MCP:
https://api.gtx.dev/mcp, documented at https://generaltranslation.com/en-US/docs/overview/for-coding-agents - Documentation package:
@generaltranslation/mcp
Verification
verification:
status: verified
tested_at: "2026-09-21"
product_version: "hosted MCP serverInfo 2026-03-06.v1"
command: "JSON-RPC tools/call over POST https://api.gtx.dev/mcp with Authorization: Bearer, sequence: create_project, upload_source_files, enqueue_file_translations, get_translation_job_info, download_files"
expected_result: "the job reaches completed and download_files returns base64 content in the target locale with the source key set intact"
observed_in_run:
server_info: '{"name":"generaltranslation","title":"General Translation API","version":"2026-03-06.v1"}'
tool_count: 24
transport: "streamable HTTP, replies as server-sent events, no Mcp-Session-Id returned"
create_project: "prj_q8cb6yw0o5vwecar48eaf2he"
upload: "returned fileId, versionId and branchId for web/en.json"
enqueue: "job ftq_ktteezb6vjg3nd950jt8yurc for locale de"
job_status: "processing then completed, about 13 seconds"
download: 'base64 under `data`, decoding to {"nav.workspace":"Arbeitsbereich","billing.seat":"Sitzplatz","action.sync":"Jetzt synchronisieren"}'
note:
"Tool names and schemas were read from this server's own tools/list, not carried from documentation. Re-run tools/list against the server you connect to; the surface changes between releases. The 24-tool count is an observation from the tested date, not a contract."
cross_reference:
"This project had no Context Group assigned, and Seat came back as Sitzplatz. The same source term renders as Lizenzplatz in the terminology example, where an explicit glossary governs it."
