@i18n-agent/mcp-client 1.8.463 → 1.9.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.
Files changed (3) hide show
  1. package/install.js +328 -11
  2. package/mcp-client.js +16 -33
  3. package/package.json +1 -1
package/install.js CHANGED
@@ -153,21 +153,40 @@ function checkExistingApiKey(configPath) {
153
153
  }
154
154
  }
155
155
 
156
- async function checkExistingApiKeys(availableIDEs) {
156
+ async function checkExistingApiKeys(availableIDEs, claudeCodeCLIAvailable = false, codexCLIAvailable = false) {
157
157
  const withKeys = [];
158
158
  const withoutKeys = [];
159
159
 
160
160
  for (const ide of availableIDEs) {
161
- // Also check ~/.claude.json for Claude Code CLI
162
- if (ide.key === 'claude-code') {
163
- const claudeJsonPath = path.join(os.homedir(), '.claude.json');
164
- if (checkExistingApiKey(claudeJsonPath)) {
165
- withKeys.push(ide);
166
- continue;
161
+ let hasKey = false;
162
+
163
+ // Check CLI registrations first for native CLI tools
164
+ if (ide.key === 'claude-code' && claudeCodeCLIAvailable) {
165
+ const cliKey = getClaudeCodeExistingApiKey('i18n-agent');
166
+ if (cliKey) {
167
+ hasKey = true;
168
+ }
169
+ } else if (ide.key === 'codex' && codexCLIAvailable) {
170
+ const cliKey = getCodexExistingApiKey('i18n-agent');
171
+ if (cliKey) {
172
+ hasKey = true;
173
+ }
174
+ }
175
+
176
+ // Fall back to config file check
177
+ if (!hasKey) {
178
+ // Also check ~/.claude.json for Claude Code CLI
179
+ if (ide.key === 'claude-code') {
180
+ const claudeJsonPath = path.join(os.homedir(), '.claude.json');
181
+ if (checkExistingApiKey(claudeJsonPath)) {
182
+ hasKey = true;
183
+ }
184
+ } else if (checkExistingApiKey(ide.configPath)) {
185
+ hasKey = true;
167
186
  }
168
187
  }
169
188
 
170
- if (checkExistingApiKey(ide.configPath)) {
189
+ if (hasKey) {
171
190
  withKeys.push(ide);
172
191
  } else {
173
192
  withoutKeys.push(ide);
@@ -201,7 +220,7 @@ function detectNodeEnvironment() {
201
220
  const nvmDir = process.env.NVM_DIR || path.join(os.homedir(), '.nvm');
202
221
  const nodeVersion = process.version;
203
222
  const nodePath = process.execPath;
204
-
223
+
205
224
  return {
206
225
  isNvm: nodePath.includes('.nvm') || nodePath.includes('nvm'),
207
226
  nodePath,
@@ -209,6 +228,195 @@ function detectNodeEnvironment() {
209
228
  };
210
229
  }
211
230
 
231
+ // Detect if Codex CLI is available
232
+ function isCodexCLIAvailable() {
233
+ try {
234
+ execSync('codex --version', { stdio: 'pipe' });
235
+ return true;
236
+ } catch {
237
+ return false;
238
+ }
239
+ }
240
+
241
+ // Check if MCP server is already registered in Codex
242
+ function isCodexMCPRegistered(serverName) {
243
+ try {
244
+ const result = execSync(`codex mcp get ${serverName}`, { stdio: 'pipe' });
245
+ return true;
246
+ } catch {
247
+ return false;
248
+ }
249
+ }
250
+
251
+ // Install MCP server via Codex CLI native command
252
+ function installViaCodexCLI(existingApiKey = '') {
253
+ const { mcpClientPath, packageDir } = getMcpClientPaths();
254
+ const nodeEnv = detectNodeEnvironment();
255
+
256
+ // Determine node command - use absolute path for nvm
257
+ const nodeCmd = nodeEnv.isNvm ? nodeEnv.nodePath : 'node';
258
+
259
+ // Get existing API key from CLI registration BEFORE removing
260
+ if (!existingApiKey) {
261
+ existingApiKey = getCodexExistingApiKey('i18n-agent');
262
+ if (existingApiKey) {
263
+ console.log(' 🔑 Preserving existing API key from CLI registration');
264
+ }
265
+ }
266
+
267
+ // Remove existing registration if present
268
+ if (isCodexMCPRegistered('i18n-agent')) {
269
+ try {
270
+ execSync('codex mcp remove i18n-agent', { stdio: 'pipe' });
271
+ console.log(' 🔄 Removed existing i18n-agent registration');
272
+ } catch {
273
+ // Ignore if removal fails
274
+ }
275
+ }
276
+
277
+ // Build the command with env vars
278
+ const envArgs = [
279
+ '--env', `MCP_SERVER_URL=https://mcp.i18nagent.ai`
280
+ ];
281
+
282
+ if (existingApiKey) {
283
+ envArgs.push('--env', `API_KEY=${existingApiKey}`);
284
+ } else {
285
+ envArgs.push('--env', 'API_KEY=');
286
+ }
287
+
288
+ // Build the full command
289
+ // codex mcp add [--env KEY=VALUE]... <name> <command> [args...]
290
+ const cmdParts = [
291
+ 'codex', 'mcp', 'add',
292
+ ...envArgs,
293
+ 'i18n-agent',
294
+ nodeCmd,
295
+ mcpClientPath
296
+ ];
297
+
298
+ // Execute the command
299
+ execSync(cmdParts.join(' '), {
300
+ cwd: packageDir,
301
+ stdio: 'pipe'
302
+ });
303
+
304
+ return true;
305
+ }
306
+
307
+ // Detect if Claude Code CLI is available
308
+ function isClaudeCodeCLIAvailable() {
309
+ try {
310
+ execSync('claude mcp list', { stdio: 'pipe' });
311
+ return true;
312
+ } catch {
313
+ return false;
314
+ }
315
+ }
316
+
317
+ // Check if MCP server is already registered in Claude Code CLI
318
+ function isClaudeCodeMCPRegistered(serverName) {
319
+ try {
320
+ execSync(`claude mcp get ${serverName}`, { stdio: 'pipe' });
321
+ return true;
322
+ } catch {
323
+ return false;
324
+ }
325
+ }
326
+
327
+ // Get existing API key from Claude Code CLI registration
328
+ function getClaudeCodeExistingApiKey(serverName) {
329
+ try {
330
+ const output = execSync(`claude mcp get ${serverName}`, { stdio: 'pipe', encoding: 'utf8' });
331
+ // Parse output like: API_KEY=i18n_xxx
332
+ const match = output.match(/API_KEY=([^\s,\n]+)/);
333
+ if (match && match[1] && match[1] !== '') {
334
+ return match[1];
335
+ }
336
+ } catch {
337
+ // Server not registered
338
+ }
339
+ return '';
340
+ }
341
+
342
+ // Get existing API key from Codex CLI registration
343
+ function getCodexExistingApiKey(serverName) {
344
+ try {
345
+ const output = execSync(`codex mcp list`, { stdio: 'pipe', encoding: 'utf8' });
346
+ // Parse output - Codex shows: API_KEY=i18n_xxx, MCP_SERVER_URL=...
347
+ const lines = output.split('\n');
348
+ for (const line of lines) {
349
+ if (line.includes(serverName)) {
350
+ const match = line.match(/API_KEY=([^\s,]+)/);
351
+ if (match && match[1] && match[1] !== '') {
352
+ return match[1];
353
+ }
354
+ }
355
+ }
356
+ } catch {
357
+ // Server not registered
358
+ }
359
+ return '';
360
+ }
361
+
362
+ // Install MCP server via Claude Code CLI native command
363
+ function installViaClaudeCodeCLI(existingApiKey = '', scope = 'user') {
364
+ const { mcpClientPath } = getMcpClientPaths();
365
+ const nodeEnv = detectNodeEnvironment();
366
+
367
+ // Determine node command - use absolute path for nvm
368
+ const nodeCmd = nodeEnv.isNvm ? nodeEnv.nodePath : 'node';
369
+
370
+ // Get existing API key from CLI registration BEFORE removing
371
+ if (!existingApiKey) {
372
+ existingApiKey = getClaudeCodeExistingApiKey('i18n-agent');
373
+ if (existingApiKey) {
374
+ console.log(' 🔑 Preserving existing API key from CLI registration');
375
+ }
376
+ }
377
+
378
+ // Remove existing registration if present
379
+ if (isClaudeCodeMCPRegistered('i18n-agent')) {
380
+ try {
381
+ execSync(`claude mcp remove --scope ${scope} i18n-agent`, { stdio: 'pipe' });
382
+ console.log(' 🔄 Removed existing i18n-agent registration');
383
+ } catch {
384
+ // Ignore if removal fails
385
+ }
386
+ }
387
+
388
+ // Build env args using -e format (Claude uses -e, not --env)
389
+ // Note: -e args must come AFTER the server name
390
+ const envArgs = [
391
+ '-e', `MCP_SERVER_URL=https://mcp.i18nagent.ai`
392
+ ];
393
+
394
+ if (existingApiKey) {
395
+ envArgs.push('-e', `API_KEY=${existingApiKey}`);
396
+ } else {
397
+ envArgs.push('-e', 'API_KEY=');
398
+ }
399
+
400
+ // Build the full command
401
+ // claude mcp add --transport stdio --scope user <name> -e KEY=VALUE -- <command> [args...]
402
+ // Note: <name> must come BEFORE -e options
403
+ const cmdParts = [
404
+ 'claude', 'mcp', 'add',
405
+ '--transport', 'stdio',
406
+ '--scope', scope,
407
+ 'i18n-agent', // Name must come before -e options
408
+ ...envArgs,
409
+ '--', // Separator for command arguments
410
+ nodeCmd,
411
+ mcpClientPath
412
+ ];
413
+
414
+ // Execute the command
415
+ execSync(cmdParts.join(' '), { stdio: 'pipe' });
416
+
417
+ return true;
418
+ }
419
+
212
420
  function createWrapperScript(targetDir) {
213
421
  const nodeEnv = detectNodeEnvironment();
214
422
  const wrapperPath = path.join(targetDir, 'run-mcp.sh');
@@ -407,8 +615,12 @@ For manual setup instructions, visit: https://docs.i18nagent.ai/setup
407
615
  });
408
616
  console.log('');
409
617
 
618
+ // Check if native CLIs are available (needed for API key detection)
619
+ const codexCLIAvailable = isCodexCLIAvailable();
620
+ const claudeCodeCLIAvailable = isClaudeCodeCLIAvailable();
621
+
410
622
  // Check for existing API keys BEFORE installation
411
- const { withKeys, withoutKeys } = await checkExistingApiKeys(availableIDEs);
623
+ const { withKeys, withoutKeys } = await checkExistingApiKeys(availableIDEs, claudeCodeCLIAvailable, codexCLIAvailable);
412
624
 
413
625
  if (withKeys.length > 0 && withoutKeys.length === 0) {
414
626
  console.log(`✅ API Keys Already Configured:`);
@@ -448,11 +660,92 @@ For manual setup instructions, visit: https://docs.i18nagent.ai/setup
448
660
  const idesWithApiKey = [];
449
661
  const idesNeedingApiKey = [];
450
662
 
663
+ // Show native CLI detection message (CLIs already detected above)
664
+ if (codexCLIAvailable || claudeCodeCLIAvailable) {
665
+ console.log('🔧 Native CLI support detected:');
666
+ if (claudeCodeCLIAvailable) {
667
+ console.log(' - Claude Code CLI (`claude mcp add`)');
668
+ }
669
+ if (codexCLIAvailable) {
670
+ console.log(' - Codex CLI (`codex mcp add`)');
671
+ }
672
+ console.log('');
673
+ }
674
+
451
675
  for (const ide of availableIDEs) {
452
676
  try {
453
677
  console.log(`⚙️ Configuring ${ide.name}...`);
454
678
 
455
679
  let result;
680
+
681
+ // Special handling for Claude Code CLI - use native CLI if available
682
+ if (ide.key === 'claude-code' && claudeCodeCLIAvailable) {
683
+ // Get existing API key - check CLI registration first, then config file
684
+ let existingApiKey = getClaudeCodeExistingApiKey('i18n-agent');
685
+ if (!existingApiKey && fs.existsSync(ide.configPath)) {
686
+ try {
687
+ const content = fs.readFileSync(ide.configPath, 'utf8');
688
+ const config = JSON.parse(content);
689
+ existingApiKey = config.mcpServers?.["i18n-agent"]?.env?.API_KEY || '';
690
+ } catch {
691
+ // Ignore parse errors
692
+ }
693
+ }
694
+
695
+ try {
696
+ installViaClaudeCodeCLI(existingApiKey);
697
+ console.log(`✅ ${ide.name} configured via native CLI!`);
698
+ console.log(` Run 'claude mcp list' to verify\n`);
699
+ installCount++;
700
+ installedIDEs.push(ide);
701
+
702
+ // Track API key status
703
+ if (existingApiKey) {
704
+ idesWithApiKey.push(ide);
705
+ } else {
706
+ idesNeedingApiKey.push(ide);
707
+ }
708
+ continue;
709
+ } catch (cliError) {
710
+ console.log(` ⚠️ Native CLI failed, falling back to config file...`);
711
+ // Fall through to config file method
712
+ }
713
+ }
714
+
715
+ // Special handling for Codex - use native CLI if available
716
+ if (ide.key === 'codex' && codexCLIAvailable) {
717
+ // Get existing API key - check CLI registration first, then config file
718
+ let existingApiKey = getCodexExistingApiKey('i18n-agent');
719
+ if (!existingApiKey && fs.existsSync(ide.configPath)) {
720
+ try {
721
+ const content = fs.readFileSync(ide.configPath, 'utf8');
722
+ const config = JSON.parse(content);
723
+ existingApiKey = config.mcpServers?.["i18n-agent"]?.env?.API_KEY || '';
724
+ } catch {
725
+ // Ignore parse errors
726
+ }
727
+ }
728
+
729
+ try {
730
+ installViaCodexCLI(existingApiKey);
731
+ console.log(`✅ ${ide.name} configured via native CLI!`);
732
+ console.log(` Run 'codex mcp list' to verify\n`);
733
+ installCount++;
734
+ installedIDEs.push(ide);
735
+
736
+ // Track API key status
737
+ if (existingApiKey) {
738
+ idesWithApiKey.push(ide);
739
+ } else {
740
+ idesNeedingApiKey.push(ide);
741
+ }
742
+ continue;
743
+ } catch (cliError) {
744
+ console.log(` ⚠️ Native CLI failed, falling back to config file...`);
745
+ // Fall through to config file method
746
+ }
747
+ }
748
+
456
749
  if (ide.key === 'claude' || ide.key === 'claude-code') {
457
750
  result = updateClaudeConfig(ide.configPath, ide.key);
458
751
  } else {
@@ -495,6 +788,12 @@ For manual setup instructions, visit: https://docs.i18nagent.ai/setup
495
788
  });
496
789
  console.log('');
497
790
 
791
+ // Check if CLIs need API key
792
+ const codexNeedsKey = idesNeedingApiKey.some(ide => ide.key === 'codex');
793
+ const claudeCodeNeedsKey = idesNeedingApiKey.some(ide => ide.key === 'claude-code');
794
+ const showCodexInstructions = codexNeedsKey && codexCLIAvailable;
795
+ const showClaudeCodeInstructions = claudeCodeNeedsKey && claudeCodeCLIAvailable;
796
+
498
797
  // Show setup instructions only for IDEs that need them
499
798
  console.log(`🔑 Setup Instructions
500
799
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@ -503,7 +802,25 @@ Step 1: Get your API key
503
802
  👉 Sign up or log in
504
803
  👉 Copy your API key (starts with "i18n_")
505
804
 
506
- Step 2: Add API key to the config file
805
+ Step 2: Add API key to your IDE`);
806
+
807
+ if (showClaudeCodeInstructions) {
808
+ console.log(`
809
+ For Claude Code CLI (recommended):
810
+ claude mcp remove --scope user i18n-agent
811
+ claude mcp add --transport stdio --scope user i18n-agent -e MCP_SERVER_URL=https://mcp.i18nagent.ai -e API_KEY=your_key_here -- node ~/.claude/mcp-servers/i18n-agent/mcp-client.js
812
+ `);
813
+ }
814
+
815
+ if (showCodexInstructions) {
816
+ console.log(`
817
+ For Codex CLI (recommended):
818
+ codex mcp remove i18n-agent
819
+ codex mcp add --env MCP_SERVER_URL=https://mcp.i18nagent.ai --env API_KEY=your_key_here i18n-agent node ~/.claude/mcp-servers/i18n-agent/mcp-client.js
820
+ `);
821
+ }
822
+
823
+ console.log(` For config file method:
507
824
  Open the config file and edit the "API_KEY" field:
508
825
 
509
826
  "mcpServers": {
package/mcp-client.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * Integrates with Claude Code CLI to provide translation capabilities
6
6
  */
7
7
 
8
- const MCP_CLIENT_VERSION = '1.8.461';
8
+ const MCP_CLIENT_VERSION = '1.9.1';
9
9
 
10
10
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
11
11
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
@@ -49,7 +49,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
49
49
  tools: [
50
50
  {
51
51
  name: 'translate_text',
52
- description: '⚠️ CRITICAL: For multi-language translation, use targetLanguages parameter (not targetLanguage). Translate text content with cultural adaptation using AI subagents. Supports both single and multi-language translation. For large requests (>100 texts or >50,000 characters), returns a jobId for async processing. Use check_translation_status to monitor progress and download results. Set pseudoTranslation=true for testing i18n implementations without AI cost.',
52
+ description: 'Translate text content with cultural adaptation using AI subagents. Supports single or multi-language translation via targetLanguages parameter (string for single, array for multiple). For large requests (>100 texts or >50,000 characters), returns a jobId for async processing. Use check_translation_status to monitor progress and download results. Set pseudoTranslation=true for testing i18n implementations without AI cost.',
53
53
  inputSchema: {
54
54
  type: 'object',
55
55
  properties: {
@@ -59,7 +59,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
59
59
  description: 'Array of source texts to translate (any language)',
60
60
  },
61
61
  targetLanguages: {
62
- description: '⚠️ REQUIRED: Target language(s) - can be a single string (e.g., "es") OR an array of strings (e.g., ["es", "fr", "zh-CN"]) for multi-language translation',
62
+ description: 'Target language(s) - provide a single string (e.g., "es") OR an array (e.g., ["es", "fr", "zh-CN"]) for multi-language translation. Use specific locale codes like "en-US" or "en-GB" instead of generic "en".',
63
63
  oneOf: [
64
64
  { type: 'string' },
65
65
  { type: 'array', items: { type: 'string' } }
@@ -144,7 +144,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
144
144
  },
145
145
  {
146
146
  name: 'translate_file',
147
- description: '⚠️ CRITICAL: For multi-language translation, use targetLanguages parameter (not targetLanguage). Translate file content while preserving structure and format. Supports both single and multi-language translation. Supports JSON, YAML, XML, CSV, TXT, MD, and other text files. For large files (>100KB), returns a jobId for async processing. Use check_translation_status to monitor progress and download results. Set pseudoTranslation=true for testing i18n implementations without AI cost.',
147
+ description: 'Translate file content while preserving structure and format. Supports single or multi-language translation via targetLanguages parameter (string for single, array for multiple). Supports JSON, YAML, XML, CSV, TXT, MD, and other text files. Always returns a jobId for async processing - use check_translation_status to monitor progress and download_translations to get results. Set pseudoTranslation=true for testing i18n implementations without AI cost.',
148
148
  inputSchema: {
149
149
  type: 'object',
150
150
  properties: {
@@ -163,7 +163,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
163
163
  default: 'auto',
164
164
  },
165
165
  targetLanguages: {
166
- description: '⚠️ REQUIRED: Target language(s) - can be a single string (e.g., "es") OR an array of strings (e.g., ["es", "fr", "zh-CN"]) for multi-language translation',
166
+ description: 'Target language(s) - provide a single string (e.g., "es") OR an array (e.g., ["es", "fr", "zh-CN"]) for multi-language translation. Use specific locale codes like "en-US" or "en-GB" instead of generic "en".',
167
167
  oneOf: [
168
168
  { type: 'string' },
169
169
  { type: 'array', items: { type: 'string' } }
@@ -555,17 +555,9 @@ async function handleTranslateText(args) {
555
555
  // Namespace is optional for text translation, but recommended for organizational tracking
556
556
 
557
557
  // Normalize targetLanguages - accept both string and array
558
- let targetLanguages = rawTargetLanguages;
559
- let targetLanguage = undefined;
560
-
561
- if (typeof rawTargetLanguages === 'string') {
562
- // Single language provided as string - convert to array for internal processing
563
- targetLanguages = [rawTargetLanguages];
564
- targetLanguage = rawTargetLanguages;
565
- } else if (Array.isArray(rawTargetLanguages) && rawTargetLanguages.length === 1) {
566
- // Single language provided as array - extract for backward compatibility
567
- targetLanguage = rawTargetLanguages[0];
568
- }
558
+ const targetLanguages = typeof rawTargetLanguages === 'string'
559
+ ? [rawTargetLanguages]
560
+ : rawTargetLanguages;
569
561
 
570
562
  if (!targetLanguages?.length) {
571
563
  throw new Error('targetLanguages parameter is required (can be a string for single language or array for multiple languages)');
@@ -585,7 +577,6 @@ async function handleTranslateText(args) {
585
577
  arguments: {
586
578
  apiKey: API_KEY,
587
579
  texts: texts,
588
- targetLanguage: targetLanguage,
589
580
  targetLanguages: targetLanguages,
590
581
  sourceLanguage: sourceLanguage && sourceLanguage !== 'auto' ? sourceLanguage : undefined,
591
582
  targetAudience: targetAudience,
@@ -640,12 +631,12 @@ async function handleTranslateText(args) {
640
631
  // Extract the actual translation result from the job result
641
632
  if (jobResult && jobResult.content && jobResult.content[0]) {
642
633
  const translationData = JSON.parse(jobResult.content[0].text);
643
- return formatTranslationResult(translationData, texts, targetLanguage, sourceLanguage, targetAudience, industry, region);
634
+ return formatTranslationResult(translationData, texts, targetLanguages, sourceLanguage, targetAudience, industry, region);
644
635
  }
645
636
  return jobResult;
646
637
  } else {
647
638
  // Regular synchronous result
648
- return formatTranslationResult(parsed, texts, targetLanguage, sourceLanguage, targetAudience, industry, region);
639
+ return formatTranslationResult(parsed, texts, targetLanguages, sourceLanguage, targetAudience, industry, region);
649
640
  }
650
641
  } catch {
651
642
  // Not JSON or error parsing - return as-is
@@ -894,17 +885,9 @@ async function handleTranslateFile(args) {
894
885
  }
895
886
 
896
887
  // Normalize targetLanguages - accept both string and array
897
- let targetLanguages = rawTargetLanguages;
898
- let targetLanguage = undefined;
899
-
900
- if (typeof rawTargetLanguages === 'string') {
901
- // Single language provided as string - convert to array for internal processing
902
- targetLanguages = [rawTargetLanguages];
903
- targetLanguage = rawTargetLanguages;
904
- } else if (Array.isArray(rawTargetLanguages) && rawTargetLanguages.length === 1) {
905
- // Single language provided as array - extract for backward compatibility
906
- targetLanguage = rawTargetLanguages[0];
907
- }
888
+ const targetLanguages = typeof rawTargetLanguages === 'string'
889
+ ? [rawTargetLanguages]
890
+ : rawTargetLanguages;
908
891
 
909
892
  if (!targetLanguages?.length) {
910
893
  throw new Error('targetLanguages parameter is required (can be a string for single language or array for multiple languages)');
@@ -939,7 +922,6 @@ async function handleTranslateFile(args) {
939
922
  };
940
923
 
941
924
  // Add optional parameters only if defined
942
- if (targetLanguage !== undefined) requestArgs.targetLanguage = targetLanguage;
943
925
  if (targetLanguages !== undefined) requestArgs.targetLanguages = targetLanguages;
944
926
  if (region !== undefined) requestArgs.region = region;
945
927
  if (context !== undefined) requestArgs.context = context;
@@ -1078,14 +1060,15 @@ async function handleTranslateFile(args) {
1078
1060
  }
1079
1061
 
1080
1062
  // Format translation result for consistent output
1081
- function formatTranslationResult(parsedResult, texts, targetLanguage, sourceLanguage, targetAudience, industry, region) {
1063
+ function formatTranslationResult(parsedResult, texts, targetLanguages, sourceLanguage, targetAudience, industry, region) {
1064
+ const targetLangsDisplay = Array.isArray(targetLanguages) ? targetLanguages.join(', ') : targetLanguages;
1082
1065
  return {
1083
1066
  translatedTexts: parsedResult?.translatedTexts || [],
1084
1067
  content: [
1085
1068
  {
1086
1069
  type: 'text',
1087
1070
  text: `Translation Results:\n\n` +
1088
- `🌍 ${parsedResult?.sourceLanguage || sourceLanguage || 'Auto-detected'} → ${parsedResult?.targetLanguage || targetLanguage}\n` +
1071
+ `🌍 ${parsedResult?.sourceLanguage || sourceLanguage || 'Auto-detected'} → ${parsedResult?.targetLanguages?.join(', ') || targetLangsDisplay}\n` +
1089
1072
  `👥 Audience: ${parsedResult?.targetAudience || targetAudience}\n` +
1090
1073
  `🏭 Industry: ${parsedResult?.industry || industry}\n` +
1091
1074
  `${parsedResult?.region || region ? `📍 Region: ${parsedResult?.region || region}\n` : ''}` +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@i18n-agent/mcp-client",
3
- "version": "1.8.463",
3
+ "version": "1.9.1",
4
4
  "description": "🌍 i18n-agent MCP Client - 48 languages, AI-powered translation for Claude, Claude Code, Cursor, VS Code, Codex. Get API key at https://app.i18nagent.ai",
5
5
  "main": "mcp-client.js",
6
6
  "bin": {