diff --git a/CLAUDE.md b/CLAUDE.md index 596688e..bc445e3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,22 +10,40 @@ Wraps the `smartling-cli` binary. Three tools are available: ## File access -User files are mounted at `/smartling` inside the container. Always use `/smartling/...` paths when referring to files: +User files are mounted at `/smartling` inside the container. Always use `/smartling/...` paths when referring to local files. + +### URI convention for push/pull + +When pushing a file, **always** provide an explicit `` argument that strips the `/smartling` prefix: ``` -# Correct +# Correct — URI is "en/strings.json" (relative, no /smartling prefix) +files push /smartling/en/strings.json en/strings.json --type json + +# Wrong — URI becomes "/smartling/en/strings.json" (absolute container path) files push /smartling/en/strings.json --type json +``` + +When pulling, **always** pass `--directory /smartling` so translated files are written back into the mounted directory: -# Wrong -files push ./en/strings.json +``` +# Correct +files pull '**.json' -l es-ES --directory /smartling + +# Wrong — files land in /app/smartling/... (inside container, not in mounted dir) +files pull '**.json' -l es-ES ``` +**Why this matters:** the pull format template preserves the full URI as the output path. If the URI contains `/smartling/en/strings.json` and you pull with `--directory /smartling`, Go's `filepath.Join` produces `/smartling/smartling/en/strings_es-ES.json` (double path). Keeping URIs as relative paths (e.g. `en/strings.json`) avoids this entirely. + Use `smartling-ls` (optionally with a path) to discover what files are available before operating on them. Use `smartling-cat` to inspect file contents. ## Credentials `SMARTLING_USER_ID`, `SMARTLING_SECRET`, and `SMARTLING_PROJECT_ID` are injected automatically via Docker env. Do not ask the user for credentials and do not include them in commands. +`SMARTLING_ACCOUNT_ID` is also injected automatically by the MCP server as a `-a` flag on every command. Most commands need account ID - if it is not set, `projects list`, `files push`, `glossaries *`, and other account-scoped operations will fail with "parameter `AccountUID` cannot be empty". + ## Common task patterns **List available files:** @@ -41,12 +59,12 @@ smartling-cat path=/smartling/en/strings.json **Upload a file:** ``` -smartling-cli: "files push /smartling/en/strings.json --type json" +smartling-cli: "files push /smartling/en/strings.json en/strings.json --type json" ``` **Download translations:** ``` -smartling-cli: "files pull '**.json' -l es-ES fr-FR" +smartling-cli: "files pull '**.json' -l es-ES -l fr-FR --directory /smartling" ``` **Check translation progress:** @@ -63,3 +81,27 @@ smartling-cli: "projects list -a " ``` smartling-cli: "mt translate /smartling/en/strings.json -l es-ES" ``` + +**List glossaries:** +``` +smartling-cli: "glossaries list" +smartling-cli: "glossaries list --name 'Product Terms'" +``` + +**Export a glossary:** +``` +smartling-cli: "glossaries export 'Product Terms' /smartling/glossary.xlsx --file-type xlsx" +smartling-cli: "glossaries export 'Product Terms' /smartling/glossary.csv --file-type csv --locale es-ES" +smartling-cli: "glossaries export 'Product Terms' /smartling/glossary.tbx --file-type tbx --tbx-version v3" +``` + +**Import an updated glossary:** +``` +smartling-cli: "glossaries import 'Product Terms' /smartling/glossary.xlsx" +smartling-cli: "glossaries import 'Product Terms' /smartling/glossary.xlsx --archive-mode" +``` + +**Create a glossary:** +``` +smartling-cli: "glossaries create 'Product Terms' --locale es-ES --locale fr-FR" +``` diff --git a/README.md b/README.md index bdc22b4..f4824b8 100644 --- a/README.md +++ b/README.md @@ -37,13 +37,15 @@ Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) o "-e", "SMARTLING_USER_ID", "-e", "SMARTLING_SECRET", "-e", "SMARTLING_PROJECT_ID", + "-e", "SMARTLING_ACCOUNT_ID", "-v", "/absolute/path/to/your/project:/smartling", "smartlinginc/smartling-cli-mcp" ], "env": { "SMARTLING_USER_ID": "your-user-id", "SMARTLING_SECRET": "your-secret", - "SMARTLING_PROJECT_ID": "your-project-id" + "SMARTLING_PROJECT_ID": "your-project-id", + "SMARTLING_ACCOUNT_ID": "your-account-id" } } } @@ -51,6 +53,8 @@ Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) o ``` > **Important:** The volume mount must map to `/smartling` inside the container. The `smartling-ls` and `smartling-cat` tools only work within that path. +> +> `SMARTLING_ACCOUNT_ID` is required for most commands (`files push`, `projects list`, `glossaries *`, etc.). The MCP server injects it automatically as `-a ` — it is not natively supported by the CLI as an env var. To use a custom `smartling.yml` (e.g. with file type mappings), mount it into `/app/smartling.yml` inside the container: @@ -75,13 +79,15 @@ Add to your project's `.claude/settings.json` or run `/mcp` in Claude Code: "-e", "SMARTLING_USER_ID", "-e", "SMARTLING_SECRET", "-e", "SMARTLING_PROJECT_ID", + "-e", "SMARTLING_ACCOUNT_ID", "-v", "/absolute/path/to/your/project:/smartling", "smartlinginc/smartling-cli-mcp" ], "env": { "SMARTLING_USER_ID": "your-user-id", "SMARTLING_SECRET": "your-secret", - "SMARTLING_PROJECT_ID": "your-project-id" + "SMARTLING_PROJECT_ID": "your-project-id", + "SMARTLING_ACCOUNT_ID": "your-account-id" } } } @@ -100,6 +106,7 @@ With a custom `smartling.yml`: "-e", "SMARTLING_USER_ID", "-e", "SMARTLING_SECRET", "-e", "SMARTLING_PROJECT_ID", + "-e", "SMARTLING_ACCOUNT_ID", "-v", "/absolute/path/to/your/project:/smartling", "-v", "/absolute/path/to/smartling.yml:/app/smartling.yml", "smartlinginc/smartling-cli-mcp" @@ -107,7 +114,8 @@ With a custom `smartling.yml`: "env": { "SMARTLING_USER_ID": "your-user-id", "SMARTLING_SECRET": "your-secret", - "SMARTLING_PROJECT_ID": "your-project-id" + "SMARTLING_PROJECT_ID": "your-project-id", + "SMARTLING_ACCOUNT_ID": "your-account-id" } } } @@ -120,8 +128,8 @@ Once configured, ask Claude naturally: - *"List my Smartling projects"* - *"Show files available for translation"* -- *"Push /smartling/en/strings.json to Smartling"* -- *"Pull Spanish translations for all JSON files"* +- *"Push /smartling/en/strings.json to Smartling with URI en/strings.json"* +- *"Pull Spanish translations for all JSON files into /smartling"* - *"Check translation status for my project"* - *"Machine translate /smartling/en/strings.json to French"* @@ -160,6 +168,33 @@ MT (Machine Translation) --input-directory Source directory --output-directory Output directory +GLOSSARIES + glossaries list List glossaries in the account + --name Filter by name + --output simple|table|json Output format + glossaries create Create a new glossary + --locale Add a locale (repeatable) + --description Optional description + --verification-mode Enable verification mode + --fallback-locale : Fallback locale mapping (repeatable) + glossaries export [file] Export glossary entries to a file + --file-type csv|xlsx|tbx Export file format (required) + --tbx-version v2|v3 TBX version (required when --file-type=tbx) + --focus-locale Focus locale for the export + --locale Include locale in export (repeatable) + --skip-entries Skip glossary entries in the export + --filter-query Filter entries by free-text query + --filter-entry-state Filter entries by state + --filter-locale Filter by locale ID (repeatable) + --filter-entry-uid Filter by entry UID (repeatable) + --filter-missing-translation-locale Filter: locale missing a translation + --filter-present-translation-locale Filter: locale with a translation + --filter-created-date Filter by created date + --filter-last-modified-date Filter by last modified date + glossaries import Import glossary from CSV/XLSX/TBX + --archive-mode Archive entries missing from the file + --media-type Override media type detection + GLOBAL FLAGS -a, --account Override account ID -p, --project Override project ID diff --git a/src/server.js b/src/server.js index 904909b..493f42c 100644 --- a/src/server.js +++ b/src/server.js @@ -40,10 +40,31 @@ const TOOL_DESCRIPTION = `Run any smartling-cli command. Pass arguments as a sin Run --help on any command or subcommand to see all available options, e.g. "files push --help" or "mt translate --help". +FILE URI CONVENTION + User files are mounted at /smartling inside the container. + When pushing a file, ALWAYS specify an explicit argument that strips the /smartling prefix. + This ensures the file URI stored in Smartling is a clean relative path (e.g. "en/strings.json"), + not an absolute container path (e.g. "/smartling/en/strings.json"). + Failing to do so causes double-path issues on download: pulled files would land at + /smartling/smartling/... instead of /smartling/... + When pulling files, ALWAYS pass --directory /smartling so translated files are written + back into the mounted directory at the correct path. + + Correct push: files push /smartling/en/strings.json en/strings.json + Wrong push: files push /smartling/en/strings.json ← URI becomes /smartling/en/strings.json + Correct pull: files pull '**.json' -l es-ES --directory /smartling + Wrong pull: files pull '**.json' -l es-ES ← files land in /app, not /smartling + GLOBAL FLAGS (supported by all commands) -a, --account Override account ID -p, --project Override project ID +ACCOUNT ID + Most commands require an account ID. Set the SMARTLING_ACCOUNT_ID environment variable + in the Docker config to inject it automatically. If not set, pass -a explicitly. + Note: SMARTLING_ACCOUNT_ID is not natively supported by the CLI — this MCP server injects + it as a -a flag automatically when the env var is present. + PROJECTS projects list Display all projects in the account (fields: ID, ACCOUNT, NAME, LOCALE, STATUS) projects info Show details about the current project @@ -80,18 +101,45 @@ MT (Machine Translation) --output-directory Destination directory for translated files --type Override file type detection +GLOSSARIES + glossaries list List glossaries in the account + --name Filter by name + --output simple|table|json Output format + glossaries create Create a new glossary + --locale Add a locale (repeatable) + --description Optional description + --verification-mode Enable verification mode + --fallback-locale : Fallback locale mapping (repeatable) + glossaries export [file] Export glossary entries to a file + --file-type csv|xlsx|tbx Export file format (required) + --tbx-version v2|v3 TBX version (required when --file-type=tbx) + --focus-locale Focus locale for the export + --locale Include locale in export (repeatable) + --skip-entries Skip glossary entries in the export + --filter-query Filter: free-text query to match entries + --filter-entry-state Filter: entry state to match + --filter-locale Filter: locale ID (repeatable) + --filter-entry-uid Filter: entry UID (repeatable) + --filter-missing-translation-locale Filter: locale missing a translation + --filter-present-translation-locale Filter: locale with a translation + --filter-created-date Filter: created date (e.g. 2026-01-02T15:04:05Z) + --filter-last-modified-date Filter: last modified date + glossaries import Import glossary from CSV/XLSX/TBX + --archive-mode Archive entries missing from the imported file + --media-type Override media type detection + EXAMPLES projects list projects locales --short projects locales --format='{{if .Enabled}}{{.LocaleID}}{{end}}\n' files list files list '**.json' --short - files push /smartling/en/strings.json --type json - files push /smartling/en/strings.json strings/en.json + files push /smartling/en/strings.json en/strings.json --type json + files push /smartling/en/strings.json en/strings.json files push '**.md' --type plaintext -b feature-branch files push '**.md' --branch '@auto' - files pull '**.json' -l es-ES -l fr-FR - files pull --source + files pull '**.json' -l es-ES -l fr-FR --directory /smartling + files pull --source --directory /smartling files delete '**.json' files rename old/path.json new/path.json files status @@ -99,6 +147,14 @@ EXAMPLES mt detect '*.txt' --output json mt translate document.txt -l es-ES mt translate '*.txt' -l es -l fr --output-directory /smartling/translations/ + glossaries list + glossaries list --name "Product Terms" + glossaries create "Product Terms" --locale es-ES --locale fr-FR + glossaries export "Product Terms" /smartling/glossary.xlsx --file-type xlsx + glossaries export "Product Terms" /smartling/glossary.tbx --file-type tbx --tbx-version v3 + glossaries export "Product Terms" /smartling/glossary.csv --file-type csv --locale es-ES + glossaries import "Product Terms" /smartling/glossary.xlsx + glossaries import "Product Terms" /smartling/glossary.xlsx --archive-mode Full command reference: https://github.com/Smartling/smartling-cli/wiki/Projects-command-examples @@ -111,6 +167,11 @@ export function createServer(execFileFn = execFileAsync) { async function handleToolCall({ args }) { const argsArray = splitArgs(args); + const accountId = process.env.SMARTLING_ACCOUNT_ID; + if (accountId && !argsArray.includes('-a') && !argsArray.includes('--account')) { + argsArray.unshift('-a', accountId); + } + try { const { stdout, stderr } = await execFileFn('smartling-cli', argsArray, { env: process.env }); const output = [stdout, stderr].filter(Boolean).join('\n');