# Glossa > Glossa is an AI-native translation management system: push any translation file, get every language translated by Claude, dedupe strings across apps, and let agents review, audit and improve your translations over MCP, CLI and API. ## Connect - MCP (streamable HTTP): `https://glossa-meterapp.vercel.app/api/mcp`. One tool per op. - REST: `POST https://glossa-meterapp.vercel.app/api/v1/{op}` with the op's input as JSON. `GET https://glossa-meterapp.vercel.app/api/v1` lists every op with its JSON Schema. - CLI: `npx @meterapp/glossa --json ''`, plus `push` and `pull` for files. - Auth: `Authorization: Bearer gls_…` (workspace API key; create one in the dashboard under Settings → API keys, or with `npx @meterapp/glossa login`). MCP clients may use OAuth instead; see https://glossa-meterapp.vercel.app/.well-known/oauth-protected-resource. - Errors are RFC 9457 problem+json with a `code`; see https://glossa-meterapp.vercel.app/docs/errors. Claude Code: ```bash claude mcp add --transport http glossa https://glossa-meterapp.vercel.app/api/mcp \ --header "Authorization: Bearer gls_…" ``` ## Workflows 1. Orient: `get_workspace`, `get_stats`, `list_projects`. 2. Ship strings: `push_file` (any format; new strings are deduplicated and queued for translation) → `get_job` until done → `pull_file` per locale. 3. Review queue: `claim_tasks` → decide each → `resolve_task` with one of the task's actions and a note. 4. Quality: `run_audit` for a locale with a question → `get_audit` → resolve the `audit_finding`, `rule_suggestion` and `glossary_suggestion` tasks it files. 5. Investigate: `search_units` finds a term across every source, key and translation; `get_unit` shows every key and translation of one string. 6. Billing: `get_pricing` → `get_billing` → `create_checkout_link` for a human to pay, or `subscribe` with `confirm: true` once a human approved the price and a card is saved. ## Pricing Unit: AI translation: one source string translated into one locale by Claude. Imports and human or agent edits are free. Yearly is two months free. Full price list without a credential: `GET https://glossa-meterapp.vercel.app/api/v1/get_pricing`; human page: https://glossa-meterapp.vercel.app/pricing. - Free (`free`): free. 2,000 AI translations a month, 3 members, 3 projects. For trying Glossa on one app. - Pro (`pro`): $49/month or $490/year. 25,000 AI translations a month, 10 members, unlimited projects. For a product team shipping in many languages. - Team (`team`): $199/month or $1,990/year. 150,000 AI translations a month, unlimited members, unlimited projects. For companies localizing several apps and sites. - Enterprise (`enterprise`): custom. Volume AI translations a month, unlimited members, unlimited projects. For larger volumes, security reviews and invoicing. Buying a plan: - create_checkout_link {plan, interval} returns a Stripe Checkout URL; give it to a human to pay. Nothing is charged until they complete it. - subscribe {plan, interval, confirm: true} starts or changes the plan on the card already saved for this workspace, after a human approved the price. Without a saved card it returns a checkout_url instead. - get_billing shows the current plan, usage this month and whether a card is saved. - CLI: `npx @meterapp/glossa pricing`, `npx @meterapp/glossa billing`, `npx @meterapp/glossa subscribe pro --interval year [--open]`. ## Task kinds - `merge_review` (keep_merged | split): Keys that share source text were merged into one unit; split when their meaning differs. - `merge_suggestion` (merge_into_a | merge_into_b | keep_separate): Two near-identical strings that could share one unit. - `translation_review` (approve | edit | retranslate): A translation the model was not confident about. - `audit_finding` (apply | edit | dismiss): A problem an audit found, with a proposed fix. - `rule_suggestion` (add | edit | dismiss): A standing rule an audit proposes for future translations. - `glossary_suggestion` (add | dismiss): A term an audit thinks should be fixed or never translated. - `learn_format` (keep | retire | retry_with_hint | dismiss): A template the AI wrote for an unknown file format. ## Rules - Never change, drop, add or reorder placeholders ({name}, {{count}}, %@, %1$s, …). The API rejects such translations with placeholder_mismatch. - Never translate glossary terms marked do_not_translate (brand and product names). Call list_glossary before editing translations by hand. - Prefer fixing the cause over the symptom: improve a unit's context with update_unit (retranslate=true), or add a rule with add_rule, instead of hand-editing many locales. - Split a merged unit only when the keys mean different things (verb vs noun, different screens with different meaning). Identical meaning stays merged. - Claim tasks with claim_tasks before resolving them so parallel agents do not collide; claims expire after 30 minutes. - Resolve each task with one of the actions it lists, and leave a short note explaining the decision. - Use read-only ops (search_units, get_unit, list_tasks) freely; writes are recorded in the activity log with your surface. - Never buy, change or cancel a plan on your own initiative. Quote the plan and price from get_pricing, and only call subscribe or cancel_subscription with confirm: true after a human approved it; otherwise give them the create_checkout_link URL. ## Setup ### Claude Code ```bash claude mcp add --transport http glossa https://glossa-meterapp.vercel.app/api/mcp \ --header "Authorization: Bearer gls_…" ``` ### Codex ```toml # ~/.codex/config.toml [mcp_servers.glossa] url = "https://glossa-meterapp.vercel.app/api/mcp" bearer_token_env_var = "GLOSSA_API_KEY" ``` ### Cursor (.cursor/mcp.json) ```json { "mcpServers": { "glossa": { "url": "https://glossa-meterapp.vercel.app/api/mcp", "headers": { "Authorization": "Bearer gls_…" } } } } ``` ### CLI ```bash npx @meterapp/glossa login npx @meterapp/glossa push locales/en.json --project web npx @meterapp/glossa pull --project web --locale es,fr,de npx @meterapp/glossa run_audit --locale es --question "Are brand names consistent?" ``` ## Formats - Xcode String Catalog (`xcstrings`): .xcstrings; every locale in one file. - Flutter ARB (`arb`): .arb; one file per locale. - JSON (key/value, i18next) (`json`): .json; one file per locale. - YAML (Rails i18n) (`yaml`): .yml, .yaml; one file per locale. - Apple .strings (`ios-strings`): .strings; one file per locale. - Apple .stringsdict (`ios-stringsdict`): .stringsdict; one file per locale. - Android strings.xml (`android-xml`): .xml; one file per locale. - Gettext PO/POT (`gettext-po`): .po, .pot; one file per locale. - XLIFF 1.2 / 2.0 (`xliff`): .xlf, .xliff; every locale in one file. - Java .properties (`properties`): .properties; one file per locale. - .NET RESX (`resx`): .resx; one file per locale. - CSV / TSV (multi-locale) (`csv`): .csv, .tsv; every locale in one file. A file in a format none of these read starts a `learn_format` job: the AI writes a declarative template from a sample, a `learn_format` task asks a reviewer to keep or retire it, and the kept template reads every similar file from then on. ## Errors Every failure is `application/problem+json` (RFC 9457): `type`, `title` (the code), `status`, `detail`, `code`, `request_id`. - 400 `invalid_json`: The body (or ?input for GET) is not valid JSON. - 400 `invalid_input`: The input failed the op's schema. `issues` lists each problem with its path. - 400 `invalid_action`: resolve_task got an action the task's kind does not accept. The message lists the valid ones. - 400 `invalid_slug`: The project name produced no usable slug. Use letters, digits and dashes. - 400 `text_required`: An edit action needs `text` with the corrected translation. - 400 `unknown_format`: The forced `format` id is neither built in nor learned by this workspace. - 401 `unauthorized`: No credential. Send `Authorization: Bearer gls_…`. - 401 `invalid_key`: The API key does not exist or was revoked. - 401 `invalid_token`: The OAuth access token is invalid or expired. Refresh it and retry. - 402 `payment_failed`: Stripe declined the charge for a plan change. Ask a human to update the card (create_billing_portal_link) or pay through create_checkout_link. - 403 `forbidden`: The op needs an owner or admin. - 403 `no_workspace`: The signed-in user is not a member of the requested workspace (or of any). - 404 `unknown_op`: No op has that name. `GET /api/v1` lists them. - 404 `not_found`: The project, unit, task or other record is not in this workspace. - 405 `method_not_allowed`: A write op was sent with GET. Use POST. - 409 `project_exists`: A project with that slug already exists. - 409 `task_closed`: The task was already resolved or dismissed. - 409 `format_not_ready`: The file has no working format yet. Resolve its learn_format task first. - 409 `job_missing`: The job a task refers to no longer exists. Push the file again. - 409 `already_subscribed`: The workspace is already on that plan and interval. get_billing shows the subscription. - 409 `no_subscription`: There is no paid subscription to cancel or resume. - 422 `placeholder_mismatch`: A translation's placeholders differ from the source string's. Keep every placeholder exactly. - 422 `invalid_template`: A learned format template is malformed. - 422 `template_reads_nothing`: A learned format template parses the sample into zero entries. - 500 `internal_error`: Something failed on our side. Retry, and quote `request_id` if it persists. - 503 `billing_unavailable`: Billing is not configured on this server yet (no Stripe key or price). Email support to subscribe. ## Ops reference Call any op with `POST https://glossa-meterapp.vercel.app/api/v1/{name}` and its input as JSON. Read-only ops also accept GET with `?input=`. ### get_workspace Get workspace. Read-only. Group: workspace. The workspace this credential belongs to: name, source locale and the product context every AI prompt starts with. Call first to orient yourself. Input schema: ```json { "type": "object", "properties": {} } ``` ### update_workspace Update workspace. Writes. Owner or admin only. Group: workspace. Change the workspace name, source locale or product context. The product context (what the app is, who uses it, tone) is prepended to every translation and audit prompt, so a good one improves every translation. Input schema: ```json { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 100 }, "product_context": { "description": "What the product is, its audience, voice and tone.", "type": "string", "maxLength": 8000 }, "source_locale": { "type": "string", "minLength": 2, "maxLength": 35, "description": "BCP 47 locale, e.g. es, pt-BR, zh-Hans." } } } ``` ### get_stats Get stats. Read-only. Group: workspace. Counts across the workspace (projects, units, keys, open tasks) and per-locale translation coverage per project. Input schema: ```json { "type": "object", "properties": {} } ``` ### list_projects List projects. Read-only. Group: projects. Every project (app) in the workspace with its target locales. Strings are shared across projects; files belong to one. Input schema: ```json { "type": "object", "properties": {} } ``` ### create_project Create project. Writes. Group: projects. Add a project (one app, site or service) with the locales it ships in. Push files to it with push_file. Input schema: ```json { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 100 }, "slug": { "description": "URL-safe id; derived from the name when omitted.", "type": "string", "pattern": "^[a-z0-9][a-z0-9-]{0,47}$" }, "description": { "default": "", "description": "What this app is; added to the product context for its strings.", "type": "string", "maxLength": 4000 }, "target_locales": { "default": [], "maxItems": 100, "type": "array", "items": { "type": "string", "minLength": 2, "maxLength": 35, "description": "BCP 47 locale, e.g. es, pt-BR, zh-Hans." } }, "auto_translate": { "default": true, "description": "Translate new strings into every target locale automatically.", "type": "boolean" } }, "required": [ "name" ] } ``` ### update_project Update project. Writes. Group: projects. Change a project's name, description, target locales or auto-translate. Adding a locale with auto_translate on queues translation of every existing string into it. Input schema: ```json { "type": "object", "properties": { "project": { "type": "string", "minLength": 1, "description": "Project slug or id." }, "name": { "type": "string", "minLength": 1, "maxLength": 100 }, "description": { "type": "string", "maxLength": 4000 }, "target_locales": { "maxItems": 100, "type": "array", "items": { "type": "string", "minLength": 2, "maxLength": 35, "description": "BCP 47 locale, e.g. es, pt-BR, zh-Hans." } }, "auto_translate": { "type": "boolean" } }, "required": [ "project" ] } ``` ### list_files List files. Read-only. Group: files. Files of a project. `path` is a pattern: `{locale}` (or `{android_locale}`) stands for each locale's copy, so one row covers en.json, es.json, …. Input schema: ```json { "type": "object", "properties": { "project": { "type": "string", "minLength": 1, "description": "Project slug or id." } }, "required": [ "project" ] } ``` ### push_file Push file. Writes. Group: files. Upload a translation file in any format (JSON, YAML, xcstrings, .strings, stringsdict, strings.xml, PO, XLIFF, ARB, properties, RESX, CSV, or one Glossa learned). A source-locale file creates or updates keys and dedupes their strings into shared units across every project; another locale's file imports its translations. New strings are queued for AI translation. A format nobody can read yet starts a learn_format job instead. Input schema: ```json { "type": "object", "properties": { "project": { "type": "string", "minLength": 1, "description": "Project slug or id." }, "path": { "type": "string", "minLength": 1, "maxLength": 500, "description": "Path in the repo, e.g. ios/App/en.lproj/Localizable.strings or locales/en.json. Used to infer the locale." }, "content": { "type": "string", "maxLength": 10000000, "description": "The file's text." }, "locale": { "description": "The file's locale when the path doesn't say.", "type": "string", "minLength": 2, "maxLength": 35 }, "format": { "description": "Format id to force (see list_formats); detected when omitted.", "type": "string" } }, "required": [ "project", "path", "content" ] } ``` ### pull_file Pull file. Read-only. Group: files. Render one locale of a file in its own format, ready to write to disk. Returns {path, content}. Input schema: ```json { "type": "object", "properties": { "project": { "type": "string", "minLength": 1, "description": "Project slug or id." }, "path": { "type": "string", "description": "The file's path pattern from list_files, or a concrete path such as locales/es.json." }, "locale": { "type": "string", "minLength": 2, "maxLength": 35, "description": "BCP 47 locale, e.g. es, pt-BR, zh-Hans." }, "missing": { "default": "source", "description": "What untranslated keys contain.", "type": "string", "enum": [ "source", "empty", "omit" ] }, "approved_only": { "default": false, "description": "Only ship reviewed or approved translations.", "type": "boolean" } }, "required": [ "project", "path", "locale" ] } ``` ### detect_format Detect format. Read-only. Group: formats. Which format Glossa would read a file as, with confidence and the runner-up candidates. Doesn't store anything. Input schema: ```json { "type": "object", "properties": { "filename": { "type": "string" }, "content": { "type": "string", "maxLength": 2000000 } }, "required": [ "filename", "content" ] } ``` ### list_formats List formats. Read-only. Group: formats. Built-in formats and the templates this workspace has learned, with their status. Input schema: ```json { "type": "object", "properties": {} } ``` ### learn_format Learn format. Writes. Group: formats. Ask the AI to write a template for a file format Glossa can't read yet, from a sample. Runs as a job (see get_job); the learned format is stored and reads every similar file from then on. Input schema: ```json { "type": "object", "properties": { "project": { "type": "string", "minLength": 1, "description": "Project slug or id." }, "path": { "type": "string" }, "content": { "type": "string", "maxLength": 2000000 }, "hint": { "description": "Anything that helps: which fields are keys, which are comments.", "type": "string", "maxLength": 2000 } }, "required": [ "project", "path", "content" ] } ``` ### approve_format Save or retire a format. Writes. Group: formats. Set a learned format's status (active reads files, retired stops). Optionally replace its template; the new template is validated against the stored sample first. Input schema: ```json { "type": "object", "properties": { "format": { "type": "string", "description": "Format slug or id." }, "status": { "default": "active", "type": "string", "enum": [ "active", "retired", "draft" ] }, "template": {} }, "required": [ "format" ] } ``` ### search_units Search strings. Read-only. Group: strings. Full-text and fuzzy search across every source string, key name and translation in the workspace. Use it to find how a term is translated everywhere, spot inconsistencies, or find the unit behind a key. Input schema: ```json { "type": "object", "properties": { "query": { "type": "string", "minLength": 1, "maxLength": 500 }, "locale": { "description": "Also match and return translations in this locale.", "type": "string", "minLength": 2, "maxLength": 35 }, "limit": { "default": 50, "description": "Page size, at most 200.", "type": "integer", "minimum": 1, "maximum": 200 } }, "required": [ "query" ] } ``` ### list_units List strings. Read-only. Group: strings. Page through units (deduplicated source strings) with their translation in one locale. Filter by project and by translation status, e.g. status=missing to find what still needs translating or status=machine to review AI output. Input schema: ```json { "type": "object", "properties": { "project": { "type": "string", "minLength": 1, "description": "Project slug or id." }, "locale": { "type": "string", "minLength": 2, "maxLength": 35, "description": "BCP 47 locale, e.g. es, pt-BR, zh-Hans." }, "status": { "default": "any", "type": "string", "enum": [ "any", "missing", "machine", "imported", "reviewed", "approved", "needs_review" ] }, "limit": { "default": 50, "description": "Page size, at most 200.", "type": "integer", "minimum": 1, "maximum": 200 }, "offset": { "default": 0, "type": "integer", "minimum": 0, "maximum": 9007199254740991 } }, "required": [ "locale" ] } ``` ### get_unit Get string. Read-only. Group: strings. One unit with every key that uses it (across projects and files), all its translations and their history. Input schema: ```json { "type": "object", "properties": { "unit": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$", "description": "Unit id (a deduplicated source string shared by every key that points at it)." } }, "required": [ "unit" ] } ``` ### update_unit Update string context. Writes. Group: strings. Set a unit's description (context for translators and the AI), max length or tags. A good description fixes ambiguous strings in every locale at once; pass retranslate=true to redo its machine translations with the new context. Input schema: ```json { "type": "object", "properties": { "unit": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$", "description": "Unit id (a deduplicated source string shared by every key that points at it)." }, "description": { "type": "string", "maxLength": 4000 }, "max_length": { "anyOf": [ { "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991 }, { "type": "null" } ] }, "tags": { "maxItems": 30, "type": "array", "items": { "type": "string", "maxLength": 50 } }, "retranslate": { "default": false, "type": "boolean" } }, "required": [ "unit" ] } ``` ### set_translation Set translation. Writes. Group: strings. Write or correct a unit's translation in one locale. Every key sharing the unit gets it. Defaults to status reviewed; use approved for final copy. Placeholders must match the source. Input schema: ```json { "type": "object", "properties": { "unit": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$", "description": "Unit id (a deduplicated source string shared by every key that points at it)." }, "locale": { "type": "string", "minLength": 2, "maxLength": 35, "description": "BCP 47 locale, e.g. es, pt-BR, zh-Hans." }, "text": { "type": "string", "maxLength": 20000 }, "plural": { "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": { "type": "string" } }, "status": { "default": "reviewed", "type": "string", "enum": [ "machine", "imported", "reviewed", "approved", "needs_review" ] }, "note": { "default": "", "type": "string", "maxLength": 2000 } }, "required": [ "unit", "locale", "text" ] } ``` ### review_translations Review translations. Writes. Group: strings. Mark many translations reviewed, approved or needs_review at once, by unit and locale. Input schema: ```json { "type": "object", "properties": { "locale": { "type": "string", "minLength": 2, "maxLength": 35, "description": "BCP 47 locale, e.g. es, pt-BR, zh-Hans." }, "units": { "minItems": 1, "maxItems": 500, "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$", "description": "Unit id (a deduplicated source string shared by every key that points at it)." } }, "status": { "type": "string", "enum": [ "reviewed", "approved", "needs_review" ] } }, "required": [ "locale", "units", "status" ] } ``` ### translate Translate. Writes. Group: strings. Queue AI translation. Without units: fills every missing translation of a project (or the whole workspace) in the given locales. With units: (re)translates exactly those, replacing machine output but never reviewed, approved, imported or human translations. Returns a job id (get_job). Input schema: ```json { "type": "object", "properties": { "locales": { "minItems": 1, "maxItems": 100, "type": "array", "items": { "type": "string", "minLength": 2, "maxLength": 35, "description": "BCP 47 locale, e.g. es, pt-BR, zh-Hans." } }, "project": { "type": "string", "minLength": 1, "description": "Project slug or id." }, "units": { "maxItems": 2000, "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$", "description": "Unit id (a deduplicated source string shared by every key that points at it)." } } }, "required": [ "locales" ] } ``` ### merge_units Merge strings. Writes. Group: strings. Point every key of the `merge` units at the `into` unit, so they share one translation per locale. The kept unit's translations win; a merged unit's translation fills a locale the kept unit lacks. The merged units are deleted. Input schema: ```json { "type": "object", "properties": { "into": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$", "description": "Unit id (a deduplicated source string shared by every key that points at it)." }, "merge": { "minItems": 1, "maxItems": 100, "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$", "description": "Unit id (a deduplicated source string shared by every key that points at it)." } } }, "required": [ "into", "merge" ] } ``` ### split_key Split key. Writes. Group: strings. Give one key its own unit (a copy of the source and its translations) so it can be translated differently from the other keys that shared the string. Use when the same English means different things in different places. Input schema: ```json { "type": "object", "properties": { "key_id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$", "description": "The key to split off (get_unit lists keys with ids)." }, "description": { "description": "Context that tells the new unit apart.", "type": "string", "maxLength": 4000 }, "retranslate": { "default": true, "description": "Retranslate the new unit's machine translations with its own context.", "type": "boolean" } }, "required": [ "key_id" ] } ``` ### list_tasks List review tasks. Read-only. Group: tasks. The review queue, highest priority first. Each task has a kind, a title, a payload with everything needed to decide, and the actions it accepts (merge_review: keep_merged | split; merge_suggestion: merge_into_a | merge_into_b | keep_separate; learn_format: keep | retire | retry_with_hint | dismiss; translation_review: approve | edit | retranslate; audit_finding: apply | edit | dismiss; rule_suggestion: add | edit | dismiss; glossary_suggestion: add | dismiss). Input schema: ```json { "type": "object", "properties": { "status": { "default": "open", "type": "string", "enum": [ "open", "claimed", "resolved", "dismissed" ] }, "kind": { "type": "string", "enum": [ "merge_review", "merge_suggestion", "learn_format", "translation_review", "audit_finding", "rule_suggestion", "glossary_suggestion" ] }, "limit": { "default": 50, "description": "Page size, at most 200.", "type": "integer", "minimum": 1, "maximum": 200 }, "offset": { "default": 0, "type": "integer", "minimum": 0, "maximum": 9007199254740991 } } } ``` ### claim_tasks Claim review tasks. Writes. Group: tasks. Claim up to `limit` open tasks so parallel agents don't work the same ones. Claims expire after 30 minutes. Then decide each and call resolve_task. Input schema: ```json { "type": "object", "properties": { "kind": { "type": "string", "enum": [ "merge_review", "merge_suggestion", "learn_format", "translation_review", "audit_finding", "rule_suggestion", "glossary_suggestion" ] }, "limit": { "default": 10, "type": "integer", "minimum": 1, "maximum": 50 } } } ``` ### resolve_task Resolve review task. Writes. Group: tasks. Decide a task by naming one of its actions (merge_review: keep_merged | split; merge_suggestion: merge_into_a | merge_into_b | keep_separate; learn_format: keep | retire | retry_with_hint | dismiss; translation_review: approve | edit | retranslate; audit_finding: apply | edit | dismiss; rule_suggestion: add | edit | dismiss; glossary_suggestion: add | dismiss). The action is applied (split, merge, set translation, add rule, …) and the task is closed with your note. Pass text for edit actions, body for a rule edit, key_ids to choose which keys to split, hint for retry_with_hint. Input schema: ```json { "type": "object", "properties": { "task": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "action": { "type": "string" }, "note": { "default": "", "type": "string", "maxLength": 2000 }, "text": { "type": "string", "maxLength": 20000 }, "body": { "type": "string", "maxLength": 2000 }, "key_ids": { "type": "array", "items": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "hint": { "type": "string", "maxLength": 2000 } }, "required": [ "task", "action" ] } ``` ### list_glossary List glossary. Read-only. Group: glossary. Glossary terms: brand and product names that are never translated, and terms with a fixed translation per locale. Every translation prompt includes them. Input schema: ```json { "type": "object", "properties": {} } ``` ### upsert_glossary_term Add or update glossary term. Writes. Group: glossary. Add a term or change it. do_not_translate=true for brand names; translations maps locale → required translation (merged into the existing map). Input schema: ```json { "type": "object", "properties": { "term": { "type": "string", "minLength": 1, "maxLength": 200 }, "do_not_translate": { "default": false, "type": "boolean" }, "translations": { "default": {}, "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": { "type": "string" } }, "note": { "default": "", "type": "string", "maxLength": 1000 } }, "required": [ "term" ] } ``` ### delete_glossary_term Delete glossary term. Writes. Group: glossary. Remove a glossary term; future translations no longer enforce it. Existing translations are unchanged. Input schema: ```json { "type": "object", "properties": { "term": { "type": "string" } }, "required": [ "term" ] } ``` ### list_rules List translation rules. Read-only. Group: rules. Standing instructions appended to every translation prompt, globally or for one locale. Audits propose new ones as rule_suggestion tasks. Input schema: ```json { "type": "object", "properties": { "locale": { "type": "string", "minLength": 2, "maxLength": 35, "description": "BCP 47 locale, e.g. es, pt-BR, zh-Hans." }, "include_inactive": { "default": false, "type": "boolean" } } } ``` ### add_rule Add translation rule. Writes. Group: rules. Add a standing instruction for future translations, e.g. "Spanish: use tú, never usted" or "Keep button labels under 20 characters". locale null applies to every language. Input schema: ```json { "type": "object", "properties": { "body": { "type": "string", "minLength": 3, "maxLength": 2000 }, "locale": { "default": null, "anyOf": [ { "type": "string", "minLength": 2, "maxLength": 35, "description": "BCP 47 locale, e.g. es, pt-BR, zh-Hans." }, { "type": "null" } ] }, "rationale": { "default": "", "type": "string", "maxLength": 2000 } }, "required": [ "body" ] } ``` ### update_rule Update translation rule. Writes. Group: rules. Edit a translation rule's text, or set active=false to stop adding it to translation prompts. Input schema: ```json { "type": "object", "properties": { "rule": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "body": { "type": "string", "minLength": 3, "maxLength": 2000 }, "active": { "type": "boolean" } }, "required": [ "rule" ] } ``` ### run_audit Run audit. Writes. Group: audits. Ask the AI to review one locale holistically ("How is our Spanish? Are brand names consistent?"). It answers the question, scores the locale, and files audit_finding, glossary_suggestion and rule_suggestion tasks. Runs as a job; read the result with get_audit. Input schema: ```json { "type": "object", "properties": { "locale": { "type": "string", "minLength": 2, "maxLength": 35, "description": "BCP 47 locale, e.g. es, pt-BR, zh-Hans." }, "question": { "default": "", "type": "string", "maxLength": 2000 }, "sample_size": { "default": 300, "type": "integer", "minimum": 20, "maximum": 1000 } }, "required": [ "locale" ] } ``` ### get_audit Get audit. Read-only. Group: audits. An audit's answer, score and findings (empty while its job runs; see job_status). Input schema: ```json { "type": "object", "properties": { "audit": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": [ "audit" ] } ``` ### list_audits List audits. Read-only. Group: audits. Past locale audits with their summary and score, newest first. Filter by locale. Input schema: ```json { "type": "object", "properties": { "locale": { "type": "string", "minLength": 2, "maxLength": 35, "description": "BCP 47 locale, e.g. es, pt-BR, zh-Hans." }, "limit": { "default": 50, "description": "Page size, at most 200.", "type": "integer", "minimum": 1, "maximum": 200 }, "offset": { "default": 0, "type": "integer", "minimum": 0, "maximum": 9007199254740991 } } } ``` ### get_job Get job. Read-only. Group: jobs. Status and output of a background job (translate, learn_format, audit, dedupe_scan). Input schema: ```json { "type": "object", "properties": { "job": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": [ "job" ] } ``` ### list_jobs List jobs. Read-only. Group: jobs. Recent background jobs (translate, learn_format, audit, dedupe_scan) with status and errors, newest first. Input schema: ```json { "type": "object", "properties": { "status": { "type": "string", "enum": [ "queued", "running", "done", "failed" ] }, "limit": { "default": 50, "description": "Page size, at most 200.", "type": "integer", "minimum": 1, "maximum": 200 }, "offset": { "default": 0, "type": "integer", "minimum": 0, "maximum": 9007199254740991 } } } ``` ### scan_duplicates Scan for near-duplicates. Writes. Group: strings. Queue a scan for near-duplicate strings (same meaning, slightly different text) and file merge_suggestion tasks for them. Input schema: ```json { "type": "object", "properties": { "threshold": { "default": 0.8, "type": "number", "minimum": 0.5, "maximum": 0.99 } } } ``` ### list_activity List activity. Read-only. Group: workspace. Who changed what, from which surface (dashboard, api, mcp, cli, ai), newest first. Input schema: ```json { "type": "object", "properties": { "limit": { "default": 50, "description": "Page size, at most 200.", "type": "integer", "minimum": 1, "maximum": 200 }, "offset": { "default": 0, "type": "integer", "minimum": 0, "maximum": 9007199254740991 } } } ``` ### list_api_keys List API keys. Read-only. Owner or admin only. Group: keys. API keys of this workspace (prefix only; the full key is shown once at creation). Input schema: ```json { "type": "object", "properties": {} } ``` ### create_api_key Create API key. Writes. Owner or admin only. Group: keys. Create a workspace API key for the CLI, MCP or CI. The full key (gls_…) is returned only once; store it as a secret. Input schema: ```json { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 100 } }, "required": [ "name" ] } ``` ### revoke_api_key Revoke API key. Writes. Owner or admin only. Group: keys. Revoke an API key immediately; every CLI, CI job or MCP host using it stops working. Input schema: ```json { "type": "object", "properties": { "key": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" } }, "required": [ "key" ] } ``` ### get_pricing Get pricing. Read-only. Group: billing. The plans (Free, Pro, Team, Enterprise) with monthly and yearly prices in cents, AI translations included a month, member and project limits, features, and how to buy. Needs no credential over REST (GET /api/v1/get_pricing). Call before recommending or buying a plan. Input schema: ```json { "type": "object", "properties": {} } ``` ### get_billing Get billing. Read-only. Group: billing. This workspace's plan, subscription status, renewal or end date, whether a card is saved (so subscribe can charge it), and usage this month against the plan (AI translations, members, projects). Pass refresh: true right after a human finished a checkout to pull the latest state from Stripe. Input schema: ```json { "type": "object", "properties": { "refresh": { "default": false, "description": "Re-read the subscription from Stripe instead of the stored copy.", "type": "boolean" } } } ``` ### create_checkout_link Create checkout link. Writes. Owner or admin only. Group: billing. A Stripe Checkout URL where a human pays for a plan; hand it to them (or open it in their browser). Nothing is charged and the plan does not change until they complete it; then get_billing {refresh: true} shows the new plan. The link expires after 24 hours. If the workspace already has a subscription, the URL is a Stripe page where they confirm the plan change instead. Returns {url, kind: checkout | plan_change, plan, interval, amount_cents, expires_at}. Input schema: ```json { "type": "object", "properties": { "plan": { "type": "string", "enum": [ "pro", "team" ], "description": "pro or team. get_pricing lists what each includes. Enterprise is sold by email." }, "interval": { "default": "month", "description": "month, or year (two months free).", "type": "string", "enum": [ "month", "year" ] } }, "required": [ "plan" ] } ``` ### subscribe Subscribe. Writes. Owner or admin only. Group: billing. Start or change this workspace's paid plan in one call, charging the card already saved for the workspace. Only after a human approved this plan and price: confirm must be true. Without a saved card (or if the card needs the human, e.g. 3-D Secure) nothing is charged and it returns status checkout_required with a checkout_url for a human to finish. A plan change is prorated and invoiced immediately. Returns {status: subscribed | changed | unchanged | checkout_required, billing}. Input schema: ```json { "type": "object", "properties": { "plan": { "type": "string", "enum": [ "pro", "team" ], "description": "pro or team. get_pricing lists what each includes. Enterprise is sold by email." }, "interval": { "default": "month", "description": "month, or year (two months free).", "type": "string", "enum": [ "month", "year" ] }, "confirm": { "type": "boolean", "const": true, "description": "Must be true: a human has seen the plan and price and asked you to buy it. Never set it on your own initiative." } }, "required": [ "plan", "confirm" ] } ``` ### cancel_subscription Cancel subscription. Writes. Owner or admin only. Group: billing. Cancel the paid plan at the end of the current period (no refund; the plan stays until then, then the workspace is on Free). Only when a human asked: confirm must be true. resume_subscription undoes it before the period ends. Input schema: ```json { "type": "object", "properties": { "confirm": { "type": "boolean", "const": true, "description": "Must be true: a human has seen the plan and price and asked you to buy it. Never set it on your own initiative." } }, "required": [ "confirm" ] } ``` ### resume_subscription Resume subscription. Writes. Owner or admin only. Group: billing. Undo a cancellation that has not taken effect yet, so the plan renews as before. Nothing is charged until the normal renewal date. Input schema: ```json { "type": "object", "properties": {} } ``` ### create_billing_portal_link Create billing portal link. Writes. Owner or admin only. Group: billing. A private Stripe page where a human downloads invoices, updates the card or billing details, or cancels. The link is for a human and expires after a few minutes of inactivity; never open it on your own. Input schema: ```json { "type": "object", "properties": {} } ```