> ## Documentation Index
> Fetch the complete documentation index at: https://docs.textsetu.com/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP Server

> Connect Claude, Cursor, VS Code, and other MCP clients to your translations.

TextSetu ships a **remote [Model Context Protocol](https://modelcontextprotocol.io)
server** so AI agents — Claude, Cursor, VS Code Copilot, or any MCP-capable client
— can manage your translations directly: read completeness stats, search keys, add
and translate strings, import/export files, and work with branches, all in natural
language.

It's a thin layer over the [REST API](/api/introduction): every tool call goes through
`/api/v1`, so the exact same authentication, scopes, permissions, and approval
rules apply. Nothing the API won't let you do is reachable through MCP.

* **Endpoint:** `https://api.textsetu.com/mcp` (self-host: `<server>/mcp`)
* **Transport:** Streamable HTTP
* **Auth:** OAuth (approve in your browser) or a Bearer API token

## Connecting

You don't need to paste a token. Connecting runs a standard **OAuth flow** — the
first time a tool runs, TextSetu opens an **Authorize** screen in your browser;
approve it and your client caches the token. (You can still use a token directly —
see [Use a token instead](#use-a-token-instead).)

<Tabs>
  <Tab title="Claude Code">
    ```bash theme={null}
    claude mcp add --transport http textsetu https://api.textsetu.com/mcp --scope user
    ```

    Run any TextSetu tool and Claude Code walks you through the Authorize screen.
  </Tab>

  <Tab title="Cursor">
    Add to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (per-project):

    ```json theme={null}
    {
      "mcpServers": {
        "textsetu": {
          "url": "https://api.textsetu.com/mcp"
        }
      }
    }
    ```
  </Tab>

  <Tab title="VS Code">
    Add to `.vscode/mcp.json` (workspace) or your user `mcp.json`:

    ```json theme={null}
    {
      "servers": {
        "textsetu": {
          "type": "http",
          "url": "https://api.textsetu.com/mcp"
        }
      }
    }
    ```
  </Tab>

  <Tab title="Claude Desktop">
    Open **Settings → Connectors → Add custom connector**, set the URL to
    `https://api.textsetu.com/mcp`, and complete the Authorize prompt. (Remote
    connectors require a plan that supports them.)
  </Tab>

  <Tab title="ChatGPT">
    In **Settings → Connectors → Create** (enable **Developer mode** if prompted), add
    a custom connector with the MCP server URL `https://api.textsetu.com/mcp` and
    complete the Authorize prompt. Then attach the connector in the composer to use
    its tools. (Custom connectors require a paid plan and may need enabling by your
    workspace admin.)
  </Tab>

  <Tab title="Other clients">
    Any client that supports a **remote (HTTP)** MCP server works — point it at the
    endpoint:

    ```json theme={null}
    {
      "type": "http",
      "url": "https://api.textsetu.com/mcp"
    }
    ```
  </Tab>
</Tabs>

### Use a token instead

To skip the browser flow, present an API token on every request. In a
JSON-configured client, add a `headers` object to the server entry from above:

```json theme={null}
{
  "url": "https://api.textsetu.com/mcp",
  "headers": { "Authorization": "Bearer tsu_pat_…" }
}
```

Claude Code takes the same thing as a flag on `claude mcp add`:

```bash theme={null}
--header "Authorization: Bearer $TEXTSETU_TOKEN"
```

## Authentication

The MCP server accepts either TextSetu [token type](/api/introduction#authentication):

| Token                     | Prefix       | Project scope                                                                   |
| ------------------------- | ------------ | ------------------------------------------------------------------------------- |
| **Personal Access Token** | `tsu_pat_…`  | acts as you across every project you can access — pass `projectId` to each tool |
| **Project token**         | `tsu_proj_…` | bound to one project — `projectId` is optional and defaults to that project     |

When you connect through the browser **Authorize** flow, the consent screen lets
you choose exactly this: a PAT (your full access) or a project-scoped token, and
for the project token which **permissions** it may use (e.g. `translation_read`,
`translation_create`), plus an optional expiry. Prefer the narrowest grant that
does the job.

Because every tool proxies to the REST API, all the usual rules hold:

* **Writes** (`create_key`, `update_value`, `import_sources`, …) require the
  matching permission (e.g. `translation_create` / `translation_update`).
* On a project whose **main branch is protected**, direct-to-main writes are
  rejected — target a branch (`branch` argument), then `merge_branch`.
* When **approval is required**, non-approver edits land as proposals, exactly as
  they would over the API or in the app.

## Tools

### Translation memory & glossary

The tools an agent reaches for *while translating* — reuse what was translated
before, and honor the customer's terminology.

| Tool                  | What it does                                                                                                                  | Key arguments                                                           |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `get_tm_matches`      | Fuzzy TM matches with a match percentage. Call **before** translating so approved translations are reused                     | `projectId?`, `sourceTexts[]`, `languageCodes[]`                        |
| `search_tm`           | Concordance search — how a word or phrase was translated before, with full segments. Finds what whole-segment matching misses | `projectId?`, `query`, `languageCodes?`, `searchIn?`, `page?`, `limit?` |
| `lookup_glossary`     | Glossary terms occurring in the source, with translations and `doNotTranslate` / `usageStatus` flags                          | `projectId?`, `sourceTexts[]`, `languageCodes[]`                        |
| `check_glossary`      | Check a drafted translation for terminology issues. Call **after** drafting to self-review                                    | `projectId?`, `sourceText`, `targetText`, `languageCode`                |
| `pretranslate`        | Apply TM matches to untranslated strings in bulk (async)                                                                      | `projectId?`, `minMatchPercentage?`                                     |
| `pretranslate_status` | Status of a pretranslate job                                                                                                  | `projectId?`, `jobId`                                                   |

Glossary and TM **management** tools operate on organization-level resources and
therefore require a **PAT** (`tsu_pat_…`) — a project-scoped token is refused:

| Tool                            | What it does                               | Key arguments                                                            |
| ------------------------------- | ------------------------------------------ | ------------------------------------------------------------------------ |
| `list_glossaries`               | Glossaries in the org, with counts         | `orgId`                                                                  |
| `list_glossary_terms`           | Terms in a glossary (paginated)            | `glossaryId`, `search?`, `page?`, `limit?`                               |
| `create_glossary_term`          | Add a term, optionally with translations   | `glossaryId`, `term`, `translations?`, `doNotTranslate?`, `usageStatus?` |
| `update_glossary_term`          | Update a term's text, flags or notes       | `glossaryId`, `termId`, …                                                |
| `set_glossary_term_translation` | Set a term's translation in one language   | `glossaryId`, `termId`, `languageCode`, `value`                          |
| `delete_glossary_term`          | Delete a term                              | `glossaryId`, `termId`                                                   |
| `list_translation_memories`     | TMs in the org, with counts                | `orgId`                                                                  |
| `list_tm_entries`               | Entries in a TM (paginated)                | `tmId`, `search?`, `page?`, `limit?`                                     |
| `create_tm_entry`               | Add a source segment + translations        | `tmId`, `sourceText`, `context?`, `translations?`                        |
| `update_tm_entry`               | Update an entry's source text or context   | `tmId`, `entryId`, …                                                     |
| `set_tm_entry_translation`      | Set an entry's translation in one language | `tmId`, `entryId`, `languageCode`, `value`                               |
| `delete_tm_entry`               | Delete an entry                            | `tmId`, `entryId`                                                        |

### Translations, files & branches

| Tool                  | What it does                                | Key arguments                                                      |
| --------------------- | ------------------------------------------- | ------------------------------------------------------------------ |
| `list_formats`        | List supported file formats                 | —                                                                  |
| `get_stats`           | Per-language completeness for a project     | `projectId?`, `branch?`                                            |
| `list_languages`      | List a project's languages                  | `projectId?`                                                       |
| `list_keys`           | List / search keys (paginated)              | `projectId?`, `search?`, `page?`, `limit?`, `status?`, `branch?`   |
| `get_key`             | Get a key with its values                   | `projectId?`, `keyId`                                              |
| `create_key`          | Create a key, optionally with values        | `projectId?`, `key`, `description?`, `tags?`, `values?`, `branch?` |
| `update_value`        | Set a value for a key in a language         | `projectId?`, `keyId`, `lang`, `value`, `pluralForm?`, `branch?`   |
| `export_translations` | Export per-language files (base64)          | `projectId?`, `format`, `languages?`, `status?`, `branch?`         |
| `import_sources`      | Import source/translation files             | `projectId?`, `format`, `files[]`                                  |
| `list_branches`       | List a project's branches                   | `projectId?`                                                       |
| `create_branch`       | Create a branch (branching must be enabled) | `projectId?`, `name`, `description?`                               |
| `merge_branch`        | Detect conflicts, then merge into main      | `projectId?`, `branchId`                                           |

`projectId` is optional only for a project-scoped token (it defaults to that
token's project). With a PAT, pass it explicitly. `branch` targets an open
translation branch instead of main.

### AI translation

Draft translations with AI through the project's [AI engine](/docs/guides/ai-translation).
Runs are asynchronous — start one, then poll for progress.

| Tool                  | What it does                                                                     | Key arguments                                                                                                                                      |
| --------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ai_engine_info`      | Report which AI engine a project uses and whether AI translation is available    | `projectId?`                                                                                                                                       |
| `ai_translate`        | Start an AI translation run (async) into chosen languages with candidate filters | `projectId?`, `targetLanguageCodes[]`, `onlyEmpty?`, `onlyOutdated?`, `includeTags?`, `excludeTags?`, `keyPrefix?`, `branch?`, `review?`, `batch?` |
| `ai_translate_status` | Poll an AI translation run's progress and counts                                 | `projectId?`, `jobId`                                                                                                                              |

## Example prompts

Once connected, just ask — the agent picks the right tools:

* *"How complete is the French translation for project `<id>`?"* → `get_stats`
* *"Find every key containing 'checkout' that's still in draft."* → `list_keys`
* *"Add a key `cart.empty` with English 'Your cart is empty'."* → `create_key`
* *"Translate `cart.empty` into German as 'Ihr Warenkorb ist leer'."* → `update_value`
* *"Export the approved French and German translations as nested JSON."* → `export_translations`
* *"Create a branch `feature/checkout` and merge it once it's clean."* → `create_branch` / `merge_branch`

With translation memory and glossary, the agent can translate *the way you
already do*:

* *"Translate these new strings into French — reuse anything we've translated
  before and follow our glossary."* → `get_tm_matches` + `lookup_glossary`, then
  `update_value`
* *"How have we translated 'checkout' before?"* → `search_tm`
* *"Review my French translation of this string for terminology problems."* →
  `check_glossary`
* *"Fill in every French string that has a 90%+ match in the TM."* →
  `pretranslate`
* *"Add 'invoice' to the glossary as 'facture' in French."* →
  `create_glossary_term`

## Notes

* **Rate limit:** 120 requests/min per IP on the MCP endpoint (separate from the
  REST API's per-token limit).
* **Sessions** are stateful (Streamable HTTP) and idle sessions are evicted after
  30 minutes — your client re-initializes transparently.
* The server exposes standard MCP discovery, so OAuth-capable clients configure
  themselves; see the [API Reference](/api/introduction) for the underlying endpoints.
