localingos 0.1.41 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,97 @@
1
+ # MCP Directory Submission Copy
2
+
3
+ Ready-to-paste content for listing Localingos on MCP directories.
4
+
5
+ ---
6
+
7
+ ## mcp.so
8
+
9
+ Submit via GitHub Issue: https://github.com/chatmcp/mcpso/issues/new
10
+
11
+ **Title:** Add Localingos — AI translation MCP server
12
+
13
+ **Body:**
14
+
15
+ ### Server Name
16
+ Localingos
17
+
18
+ ### Server URL / Repository
19
+ https://github.com/juhsis/Localingos (source)
20
+ https://www.npmjs.com/package/localingos (npm)
21
+
22
+ ### Website
23
+ https://localingos.com
24
+ https://localingos.com/mcp (MCP-specific page)
25
+
26
+ ### Description
27
+ AI-powered translation workflow for developers. Give your AI coding agent (Cursor, Claude Desktop, Copilot, Kiro) direct access to sync translations, search keys, and manage localization — without leaving the editor. Supports 56 languages, React, Next.js, Vue, Angular.
28
+
29
+ ### Setup
30
+
31
+ **Local (stdio — built into the CLI):**
32
+ ```bash
33
+ npm install -g localingos
34
+ ```
35
+ ```json
36
+ {
37
+ "mcpServers": {
38
+ "localingos": {
39
+ "command": "localingos",
40
+ "args": ["mcp-serve"]
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ **Remote (no install needed):**
47
+ ```json
48
+ {
49
+ "mcpServers": {
50
+ "localingos": {
51
+ "type": "streamable-http",
52
+ "url": "https://mcp.localingos.com"
53
+ }
54
+ }
55
+ }
56
+ ```
57
+
58
+ ### Tools
59
+ | Tool | Description |
60
+ |---|---|
61
+ | localingos_sync | Push source strings & pull translations |
62
+ | localingos_push | Push source strings only |
63
+ | localingos_pull | Pull translations and write locale files |
64
+ | localingos_status | Show family, locales, key counts |
65
+ | localingos_search | Search keys/text across source strings |
66
+ | localingos_extract | Run extraction script |
67
+
68
+ ### Category
69
+ Developer Tools / Localization / i18n
70
+
71
+ ---
72
+
73
+ ## Smithery
74
+
75
+ Submit via https://smithery.ai — sign in, create namespace, publish server.
76
+
77
+ **Display Name:** Localingos
78
+ **Slug:** localingos
79
+ **Description:** AI-powered translation workflow — sync, push, pull, and search translations across 56 languages directly from your AI coding agent. Local (stdio) and remote (streamable-http) modes.
80
+ **Tags:** i18n, translation, localization, developer-tools, mcp
81
+ **Website:** https://localingos.com
82
+ **Repository:** https://github.com/juhsis/Localingos
83
+ **npm:** https://www.npmjs.com/package/localingos
84
+
85
+ **Remote URL:** https://mcp.localingos.com
86
+ **Transport:** streamable-http
87
+
88
+ **Local command:** `localingos mcp-serve`
89
+ **Install:** `npm install -g localingos`
90
+
91
+ ---
92
+
93
+ ## Also consider listing on
94
+
95
+ - **MCP Registry (official):** https://github.com/modelcontextprotocol/registry — the official Anthropic-backed registry. Submit via PR.
96
+ - **Glama.ai:** https://glama.ai/mcp/servers — another popular directory. Submit via their web form.
97
+ - **PulseMCP:** https://www.pulsemcp.com — curated MCP directory. Submit via web form.
package/README.md CHANGED
@@ -1,16 +1,11 @@
1
1
  # Localingos CLI
2
2
 
3
- Sync translations between your codebase and [Localingos](https://localingos.com) with a single command.
4
-
5
- ## Quick Start
3
+ AI-powered translation for developers. Localize your app into 56 languages with one command.
6
4
 
7
5
  ```bash
8
- # Install globally
9
6
  npm install -g localingos
10
-
11
- # Or use with npx (no install needed)
12
- npx localingos init
13
- npx localingos sync
7
+ localingos init
8
+ localingos sync
14
9
  ```
15
10
 
16
11
  ## Setup
@@ -25,12 +20,15 @@ Go to [localingos.com](https://localingos.com) → **Developer Tools** → **API
25
20
  localingos init
26
21
  ```
27
22
 
28
- This will interactively create a `.localingos.json` config file:
23
+ This creates two config files:
24
+
25
+ - **`localingos.config.json`** — commit this. Contains project settings (no secrets).
26
+ - **`.localingos.json`** — gitignored. Contains your API key for local development.
29
27
 
30
28
  ```json
29
+ // localingos.config.json
31
30
  {
32
- "apiKey": "your-api-key",
33
- "familyId": "your-family-id",
31
+ "projectId": "your-project-id",
34
32
  "sourceLocale": "en-US",
35
33
  "format": "json-nested",
36
34
  "sourceFile": "./src/i18n/en-US.json",
@@ -39,22 +37,18 @@ This will interactively create a `.localingos.json` config file:
39
37
  }
40
38
  ```
41
39
 
42
- > ⚠️ Add `.localingos.json` to your `.gitignore` — it contains your API key.
43
-
44
40
  If the source file doesn't exist yet, `init` will offer you three options:
45
- - **🤖 Generate an AI prompt** to automatically extract all strings from your codebase
46
- - **📝 Create a sample file** with hello-world entries to try the flow
41
+ - **🤖 Generate an AI prompt** to extract all strings from your codebase
42
+ - **📝 Create a sample file** with hello-world entries
47
43
  - **⏭️ Skip** and create it yourself
48
44
 
49
45
  ### 3. Extract translatable strings (new projects)
50
46
 
51
- If you're internationalizing an existing codebase, use:
52
-
53
47
  ```bash
54
48
  localingos extract
55
49
  ```
56
50
 
57
- This detects your project type (React, Next.js, Vue, Angular, etc.) and generates a tailored AI prompt that you paste into your AI assistant (Cline, Cursor, Copilot, ChatGPT). The AI will:
51
+ Detects your project type (React, Next.js, Vue, Angular, etc.) and generates a tailored AI prompt. Paste it into your AI assistant (Cursor, Copilot, Claude, ChatGPT) and it will:
58
52
  1. Scan your codebase for all user-facing strings
59
53
  2. Create the source locale file (e.g., `en-US.json`)
60
54
  3. Replace hardcoded strings with `t('key')` calls
@@ -66,63 +60,96 @@ This detects your project type (React, Next.js, Vue, Angular, etc.) and generate
66
60
  localingos sync
67
61
  ```
68
62
 
69
- This single command will:
70
- 1. **Extract** all translatable strings from your source file
71
- 2. **Push** them to Localingos (creates new keys, updates changed ones)
72
- 3. **Pull** all available translations
73
- 4. **Write** translation files per locale (e.g., `es-ES.json`, `fr-FR.json`)
63
+ One command to:
64
+ 1. **Push** source strings to Localingos
65
+ 2. **Pull** all available translations
66
+ 3. **Write** locale files (e.g., `es-ES.json`, `fr-FR.json`, `zh-CN.json`)
74
67
 
75
68
  ## Commands
76
69
 
77
- ### `localingos init`
70
+ | Command | Description |
71
+ |---------|-------------|
72
+ | `localingos init` | Interactive setup wizard |
73
+ | `localingos extract` | Generate AI prompt to extract translatable strings |
74
+ | `localingos sync` | Push source strings and pull translations |
75
+ | `localingos push` | Push source strings only |
76
+ | `localingos pull` | Pull translations and write locale files |
77
+ | `localingos mcp-serve` | Start MCP server for AI coding agents |
78
78
 
79
- Interactive setup wizard. Creates `.localingos.json` in the current directory.
79
+ ### Options
80
80
 
81
- ### `localingos extract`
82
-
83
- Generates an AI prompt to extract all translatable strings from your codebase.
84
-
85
- ```bash
86
- localingos extract # Detects project type, generates prompt, copies to clipboard
87
- ```
81
+ | Option | Description |
82
+ |--------|-------------|
83
+ | `--dry-run` | Preview without making API calls (sync/push) |
84
+ | `--prune` | Remove stale keys from locale files (sync) |
85
+ | `-c, --config <path>` | Path to config file |
86
+ | `-V, --version` | Show version |
87
+ | `-h, --help` | Show help |
88
88
 
89
- ### `localingos sync`
89
+ ## MCP Server
90
90
 
91
- The main command. Pushes source strings and pulls translations in one call.
91
+ The CLI includes a built-in [Model Context Protocol](https://modelcontextprotocol.io) server. Give your AI coding agent direct access to your translation workflow.
92
92
 
93
93
  ```bash
94
- localingos sync # Full sync
95
- localingos sync --dry-run # Preview what would be synced
94
+ localingos mcp-serve
96
95
  ```
97
96
 
98
- ### `localingos push`
97
+ Configure your AI agent:
99
98
 
100
- Push source strings only (no pull).
101
-
102
- ```bash
103
- localingos push # Push source strings
104
- localingos push --dry-run # Preview what would be pushed
99
+ **Cursor** (`.cursor/mcp.json`):
100
+ ```json
101
+ {
102
+ "mcpServers": {
103
+ "localingos": { "command": "localingos", "args": ["mcp-serve"] }
104
+ }
105
+ }
105
106
  ```
106
107
 
107
- ### `localingos pull`
108
-
109
- Pull translations only (no push).
110
-
111
- ```bash
112
- localingos pull # Pull and write translation files
108
+ **Claude Desktop** (`claude_desktop_config.json`):
109
+ ```json
110
+ {
111
+ "mcpServers": {
112
+ "localingos": { "command": "localingos", "args": ["mcp-serve"], "cwd": "/path/to/your/project" }
113
+ }
114
+ }
113
115
  ```
114
116
 
115
- ### Common Options
116
-
117
- | Option | Description |
118
- |--------|-------------|
119
- | `-c, --config <path>` | Path to config file (default: `.localingos.json`) |
120
- | `--env <environment>` | API environment: `prod`, `gamma`, `local` (default: `prod`) |
121
- | `--dry-run` | Preview without making API calls (sync/push) |
122
- | `-V, --version` | Show version |
123
- | `-h, --help` | Show help |
117
+ **Remote (no install needed):**
118
+ ```json
119
+ {
120
+ "mcpServers": {
121
+ "localingos": {
122
+ "type": "streamable-http",
123
+ "url": "https://mcp.localingos.com?apiKey=YOUR_KEY&projectId=YOUR_PROJECT_ID"
124
+ }
125
+ }
126
+ }
127
+ ```
124
128
 
125
- The `--env` flag is for internal development. You can also set the `LOCALINGOS_ENV` environment variable.
129
+ ### Available Tools (20)
130
+
131
+ | Tool | Description |
132
+ |---|---|
133
+ | `localingos_sync` | Push source strings & pull translations |
134
+ | `localingos_push` | Push source strings only |
135
+ | `localingos_pull` | Pull translations and write locale files |
136
+ | `localingos_status` | Show project, locales, key counts |
137
+ | `localingos_search` | Search keys/text across source strings |
138
+ | `localingos_extract` | Run i18n extraction script |
139
+ | `localingos_list_projects` | List all projects you have access to |
140
+ | `localingos_get_translatables` | Get all source strings for a project |
141
+ | `localingos_get_translations` | Get all translations for a project |
142
+ | `localingos_get_translation` | Get translations for a single key |
143
+ | `localingos_get_translations_batch` | Get translations for multiple keys |
144
+ | `localingos_update_translations` | Create or update translations manually |
145
+ | `localingos_delete_translatables` | Delete source strings (prune stale keys) |
146
+ | `localingos_translation_jobs` | List translation jobs, filter by state |
147
+ | `localingos_retry_job` | Retry a single failed translation job |
148
+ | `localingos_retry_all_jobs` | Retry all failed/blocked jobs |
149
+ | `localingos_project_members` | List members of a project |
150
+ | `localingos_add_member` | Add a member to a project |
151
+ | `localingos_remove_member` | Remove a member |
152
+ | `localingos_billing_status` | Show billing status, plan, and usage |
126
153
 
127
154
  ## Supported Formats
128
155
 
@@ -131,143 +158,72 @@ The `--env` flag is for internal development. You can also set the `LOCALINGOS_E
131
158
  | `json-nested` | Nested JSON objects | `{ "welcome": { "title": "Hello" } }` |
132
159
  | `json-flat` | Flat key-value JSON | `{ "welcome.title": "Hello" }` |
133
160
 
134
- More formats (YAML, properties, XLIFF) coming soon.
135
-
136
161
  ## CI/CD Integration
137
162
 
138
- Add to your build pipeline to keep translations in sync:
163
+ ### GitHub Actions Auto-sync on push
164
+
165
+ Automatically sync translations when source files change on `main`:
166
+
167
+ 1. Add your API key as a repository secret: `LOCALINGOS_API_KEY`
168
+ 2. Commit `localingos.config.json` (project config, no secrets)
169
+ 3. Copy [`examples/github-actions-sync.yml`](examples/github-actions-sync.yml) to `.github/workflows/localingos-sync.yml`
139
170
 
140
171
  ```yaml
141
- # GitHub Actions example
172
+ # Minimal example
142
173
  - name: Sync translations
143
174
  run: npx localingos sync
144
175
  env:
145
- # Store API key as a secret, override config
146
176
  LOCALINGOS_API_KEY: ${{ secrets.LOCALINGOS_API_KEY }}
147
177
  ```
148
178
 
149
- ## How It Works
150
-
151
- ```
152
- Your codebase Localingos
153
- ┌──────────────┐ ┌──────────────┐
154
- │ en-US.json │ ──── push ──→ │ Process & │
155
- │ (source) │ │ Translate │
156
- │ │ ←── pull ──── │ (AI-powered) │
157
- │ es-ES.json │ │ │
158
- │ fr-FR.json │ │ │
159
- │ de-DE.json │ │ │
160
- └──────────────┘ └──────────────┘
161
- ```
162
-
163
- The `sync` command combines push + pull into a single API call for efficiency. New/changed strings are registered for translation processing. Already-translated strings are returned immediately.
164
-
165
- ## Publishing to npm
166
-
167
- ### First-time setup
168
-
169
- ```bash
170
- npm login # Log in to your npmjs.com account (requires 2FA)
171
- ```
172
-
173
- ### Publishing a new version
174
-
175
- From the `localingos-cli/` directory:
176
-
177
- ```bash
178
- # Patch release (0.1.0 → 0.1.1) — bug fixes
179
- npm version patch && npm publish
180
-
181
- # Minor release (0.1.0 → 0.2.0) — new features
182
- npm version minor && npm publish
183
-
184
- # Major release (0.1.0 → 1.0.0) — breaking changes
185
- npm version major && npm publish
186
- ```
187
-
188
- `npm version` automatically:
189
- - Bumps the version in `package.json`
190
- - Creates a git commit with message `v0.2.0`
191
- - Creates a git tag `v0.2.0`
192
-
193
- After publishing, push the tag to git:
194
-
195
- ```bash
196
- git push && git push --tags
197
- ```
198
-
199
- ### Quick reference
200
-
201
- | Command | When to use | Example |
202
- |---------|------------|---------|
203
- | `npm version patch` | Bug fix, typo, minor tweak | 0.1.0 → 0.1.1 |
204
- | `npm version minor` | New feature, new format support | 0.1.0 → 0.2.0 |
205
- | `npm version major` | Breaking config/API change | 0.1.0 → 1.0.0 |
206
-
207
- ## CI/CD Integration
208
-
209
- ### GitHub Actions — Auto-sync on push
210
-
211
- Automatically sync translations when `.messages.ts` files change on `main`:
212
-
213
- 1. Add your API key as a repository secret: `LOCALINGOS_API_KEY`
214
- (Settings → Secrets and variables → Actions → New repository secret)
215
-
216
- 2. Make sure `localingos.config.json` is committed (project config, no secrets).
217
- `.localingos.json` stays in `.gitignore` (contains API key for local dev only).
218
-
219
- 3. Copy [`examples/github-actions-sync.yml`](examples/github-actions-sync.yml) to `.github/workflows/localingos-sync.yml`
220
-
221
- This workflow:
222
- - Triggers when `.messages.ts` or `src/i18n/` files change on `main`
223
- - Runs `localingos sync --prune` to push source strings and pull translations
224
- - Commits updated translation files back to the repo
225
-
226
179
  ### GitHub Actions — PR check
227
180
 
228
- Fail PRs if i18n source files are out of date or orphan messages exist:
181
+ Fail PRs if i18n source files are out of date:
229
182
 
230
183
  Copy [`examples/github-actions-check.yml`](examples/github-actions-check.yml) to `.github/workflows/localingos-check.yml`
231
184
 
232
- This runs `npm run i18n:check` which verifies:
233
- - `en-US.json` and `en-US.descriptions.json` match the `.messages.ts` files
234
- - No orphan messages (defined but never referenced in code)
235
-
236
185
  ### Environment variable
237
186
 
238
- The CLI supports `LOCALINGOS_API_KEY` as an environment variable, which takes precedence over the `apiKey` field in `.localingos.json`. This lets you keep the API key out of your repo:
239
-
240
- When you run `localingos init`, it creates two files:
241
-
242
- - **`localingos.config.json`** — commit this. Contains project settings (familyId, locale, paths). No secrets.
243
- - **`.localingos.json`** — gitignored. Contains only your `apiKey` for local development.
187
+ The CLI resolves the API key in this order:
188
+ 1. `apiKeyEnv` custom env var (if set in config)
189
+ 2. `LOCALINGOS_API_KEY` env var
190
+ 3. `apiKey` in `.localingos.json`
244
191
 
245
- ```bash
246
- # Local dev — the CLI reads apiKey from .localingos.json automatically
247
- localingos sync
248
-
249
- # CI — set as a GitHub secret, the CLI reads it from the env var
250
- LOCALINGOS_API_KEY=your-key localingos sync
251
- ```
192
+ ### Monorepo support
252
193
 
253
- ### Multiple projects (monorepo)
254
-
255
- If you have multiple projects in one repo, each with its own `localingos.config.json` and API key, add an `apiKeyEnv` field to each config pointing to a different env var:
194
+ Each project gets its own `localingos.config.json` with a different `apiKeyEnv`:
256
195
 
257
196
  ```json
258
197
  // apps/web/localingos.config.json
259
- { "apiKeyEnv": "LOCALINGOS_KEY_WEB", "familyId": "...", ... }
198
+ { "apiKeyEnv": "LOCALINGOS_KEY_WEB", "projectId": "...", ... }
260
199
 
261
200
  // apps/docs/localingos.config.json
262
- { "apiKeyEnv": "LOCALINGOS_KEY_DOCS", "familyId": "...", ... }
201
+ { "apiKeyEnv": "LOCALINGOS_KEY_DOCS", "projectId": "...", ... }
263
202
  ```
264
203
 
265
- Then in your workflow, pass each secret:
266
-
267
204
  ```yaml
268
205
  env:
269
206
  LOCALINGOS_KEY_WEB: ${{ secrets.LOCALINGOS_KEY_WEB }}
270
207
  LOCALINGOS_KEY_DOCS: ${{ secrets.LOCALINGOS_KEY_DOCS }}
271
208
  ```
272
209
 
273
- The CLI checks `apiKeyEnv` first, then falls back to `LOCALINGOS_API_KEY`, then `apiKey` in the config file.
210
+ ## How It Works
211
+
212
+ ```
213
+ Your codebase Localingos
214
+ ┌──────────────┐ ┌──────────────┐
215
+ │ en-US.json │ ──── push ──→ │ Process & │
216
+ │ (source) │ │ Translate │
217
+ │ │ ←── pull ──── │ (AI-powered) │
218
+ │ es-ES.json │ │ │
219
+ │ fr-FR.json │ │ │
220
+ │ zh-CN.json │ │ │
221
+ └──────────────┘ └──────────────┘
222
+ ```
223
+
224
+ ## Links
225
+
226
+ - [Website](https://localingos.com)
227
+ - [Documentation](https://localingos.com/docs)
228
+ - [MCP Server](https://localingos.com/mcp)
229
+ - [Pricing](https://localingos.com/pricing)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "localingos",
3
- "version": "0.1.41",
4
- "description": "CLI tool to sync translations with Localingos",
3
+ "version": "1.0.1",
4
+ "description": "AI-powered translation CLI localize your app into 56 languages with one command. Supports React, Next.js, Vue, Angular. MCP server included.",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
7
7
  "bin": {
@@ -12,10 +12,21 @@
12
12
  },
13
13
  "keywords": [
14
14
  "i18n",
15
+ "internationalization",
15
16
  "translation",
16
17
  "localization",
18
+ "l10n",
17
19
  "cli",
18
- "mcp"
20
+ "mcp",
21
+ "model-context-protocol",
22
+ "ai-translation",
23
+ "react-i18next",
24
+ "next-intl",
25
+ "react-intl",
26
+ "json-i18n",
27
+ "locale",
28
+ "multilingual",
29
+ "bedrock"
19
30
  ],
20
31
  "license": "MIT",
21
32
  "dependencies": {
@@ -257,6 +257,108 @@ export async function mcpServeCommand() {
257
257
  }
258
258
  );
259
259
 
260
+ // ── Project discovery ──────────────────────────────────────────────────
261
+
262
+ server.tool('localingos_list_projects', 'List all projects the authenticated user has access to.', {}, async () => {
263
+ const result = await api('GET', '/project', apiUrl, config.apiKey);
264
+ const projects = result.projects || [];
265
+ if (!projects.length) return { content: [{ type: 'text', text: 'No projects found.' }] };
266
+ const lines = projects.map(p => `${p.id} — ${p.name || '(unnamed)'}${p.locales?.length ? ` [${p.locales.join(', ')}]` : ''}`);
267
+ return { content: [{ type: 'text', text: `${projects.length} project(s):\n${lines.join('\n')}` }] };
268
+ });
269
+
270
+ // ── Translatable management ────────────────────────────────────────────
271
+
272
+ server.tool('localingos_delete_translatables',
273
+ 'Delete source strings from the project by their foreign IDs. Used for pruning stale keys.',
274
+ { foreignIds: z.array(z.string()).describe('List of foreign IDs (key names) to delete') },
275
+ async ({ foreignIds }) => {
276
+ try {
277
+ await api('DELETE', `/translatable/${config.projectId}`, apiUrl, config.apiKey, { foreignIds });
278
+ return { content: [{ type: 'text', text: `Deleted ${foreignIds.length} translatable(s)` }] };
279
+ } catch (e) { return { content: [{ type: 'text', text: `Failed: ${e.message}` }] }; }
280
+ }
281
+ );
282
+
283
+ // ── Translation tools ──────────────────────────────────────────────────
284
+
285
+ server.tool('localingos_get_translation',
286
+ 'Get all translations for a single source string (by foreign ID / key name).',
287
+ { foreignId: z.string().describe('The key name (foreign ID) of the source string') },
288
+ async ({ foreignId }) => {
289
+ const translations = await api('GET', `/translation/${config.projectId}/${encodeURIComponent(foreignId)}`, apiUrl, config.apiKey);
290
+ if (!translations?.length) return { content: [{ type: 'text', text: `No translations found for "${foreignId}"` }] };
291
+ const lines = translations.map(t => `${t.locale}: "${t.text}"`);
292
+ return { content: [{ type: 'text', text: `${translations.length} translation(s) for "${foreignId}":\n${lines.join('\n')}` }] };
293
+ }
294
+ );
295
+
296
+ server.tool('localingos_get_translations_batch',
297
+ 'Get translations for multiple source strings at once.',
298
+ { foreignIds: z.array(z.string()).describe('List of key names to fetch translations for') },
299
+ async ({ foreignIds }) => {
300
+ const translations = await api('POST', `/translation/${config.projectId}/batch`, apiUrl, config.apiKey, { foreignIds });
301
+ if (!translations?.length) return { content: [{ type: 'text', text: 'No translations found.' }] };
302
+ const byKey = {};
303
+ for (const t of translations) (byKey[t.foreignId] ||= []).push(t);
304
+ const lines = Object.entries(byKey).map(([k, ts]) => `${k}: ${ts.map(t => t.locale).join(', ')}`);
305
+ return { content: [{ type: 'text', text: `${translations.length} translation(s) across ${Object.keys(byKey).length} key(s):\n${lines.join('\n')}` }] };
306
+ }
307
+ );
308
+
309
+ server.tool('localingos_update_translations',
310
+ 'Create or update translations manually. Useful for correcting AI translations or adding human translations.',
311
+ { translations: z.array(z.object({
312
+ foreignId: z.string().describe('Key name of the source string'),
313
+ locale: z.string().describe('Target locale code (e.g. es-ES, fr-FR)'),
314
+ text: z.string().describe('The translated text'),
315
+ })).describe('List of translations to create or update') },
316
+ async ({ translations }) => {
317
+ try {
318
+ const translationList = translations.map(t => ({ ...t, projectId: config.projectId }));
319
+ await api('POST', '/translation', apiUrl, config.apiKey, { projectId: config.projectId, translationList });
320
+ return { content: [{ type: 'text', text: `Updated ${translations.length} translation(s)` }] };
321
+ } catch (e) { return { content: [{ type: 'text', text: `Failed: ${e.message}` }] }; }
322
+ }
323
+ );
324
+
325
+ // ── Translation job tools ──────────────────────────────────────────────
326
+
327
+ server.tool('localingos_translation_jobs',
328
+ 'List translation jobs for the project. Optionally filter by state (QUEUED, IN_PROGRESS, DONE, FAILED, BLOCKED).',
329
+ { state: z.enum(['QUEUED', 'IN_PROGRESS', 'DONE', 'FAILED', 'BLOCKED']).optional().describe('Filter by job state'),
330
+ pageSize: z.number().min(1).max(100).optional().default(20).describe('Number of results per page'),
331
+ },
332
+ async ({ state, pageSize }) => {
333
+ const urlPath = state ? `/translation-jobs/project/${config.projectId}/state/${state}` : `/translation-jobs/project/${config.projectId}`;
334
+ const result = await api('POST', urlPath, apiUrl, config.apiKey, { pageSize, nextToken: null });
335
+ const jobs = result.jobs || [];
336
+ if (!jobs.length) return { content: [{ type: 'text', text: `No ${state || ''} jobs found.` }] };
337
+ const lines = jobs.map(j => `${j.id} [${j.jobState}] ${j.sourceLocale}→${(j.targetLocales || []).join(',')} ${j.errorMessage ? '⚠ ' + j.errorMessage : ''}`);
338
+ const footer = result.hasMore ? `\n... and more (${result.totalCount} total)` : '';
339
+ return { content: [{ type: 'text', text: `${result.totalCount} job(s)${state ? ` (${state})` : ''}:\n${lines.join('\n')}${footer}` }] };
340
+ }
341
+ );
342
+
343
+ server.tool('localingos_retry_job',
344
+ 'Retry a single failed translation job.',
345
+ { jobId: z.string().describe('The job ID to retry'), jobType: z.string().describe('The job type (e.g. TRANSLATE)') },
346
+ async ({ jobId, jobType }) => {
347
+ const result = await api('POST', `/translation-jobs/job/${jobId}/${jobType}/retry`, apiUrl, config.apiKey);
348
+ if (!result.success) return { content: [{ type: 'text', text: `Retry failed: ${result.message}` }] };
349
+ return { content: [{ type: 'text', text: `Job ${jobId} queued for retry` }] };
350
+ }
351
+ );
352
+
353
+ server.tool('localingos_retry_all_jobs',
354
+ 'Retry all failed and blocked translation jobs in the project.',
355
+ {},
356
+ async () => {
357
+ const result = await api('POST', `/translation-jobs/project/${config.projectId}/retry-all`, apiUrl, config.apiKey);
358
+ return { content: [{ type: 'text', text: result.message || `Retried ${result.totalProcessed} jobs` }] };
359
+ }
360
+ );
361
+
260
362
  const transport = new StdioServerTransport();
261
363
  await server.connect(transport);
262
364
  }