@i18n-agent/mcp-client 1.8.17 → 1.8.19

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 +12 -6
  2. package/mcp-client.js +129 -39
  3. package/package.json +1 -1
package/install.js CHANGED
@@ -184,7 +184,8 @@ function createMCPConfig() {
184
184
  env: {
185
185
  MCP_SERVER_URL: "https://mcp.i18nagent.ai",
186
186
  API_KEY: ""
187
- }
187
+ },
188
+ disabled: false
188
189
  }
189
190
  }
190
191
  };
@@ -272,7 +273,8 @@ function updateClaudeConfig(configPath, ideKey = 'claude') {
272
273
  env: {
273
274
  MCP_SERVER_URL: "https://mcp.i18nagent.ai",
274
275
  API_KEY: existingApiKey || ""
275
- }
276
+ },
277
+ disabled: false
276
278
  };
277
279
  } else {
278
280
  // For system node, use 'node' with args
@@ -294,7 +296,8 @@ function updateClaudeConfig(configPath, ideKey = 'claude') {
294
296
  env: {
295
297
  MCP_SERVER_URL: "https://mcp.i18nagent.ai",
296
298
  API_KEY: existingApiKey || ""
297
- }
299
+ },
300
+ disabled: false
298
301
  };
299
302
  } else {
300
303
  const baseConfig = createMCPConfig();
@@ -350,7 +353,8 @@ function updateGenericMCPConfig(configPath) {
350
353
  env: {
351
354
  MCP_SERVER_URL: "https://mcp.i18nagent.ai",
352
355
  API_KEY: existingApiKey || ""
353
- }
356
+ },
357
+ disabled: false
354
358
  };
355
359
  } else {
356
360
  const baseConfig = createMCPConfig();
@@ -402,7 +406,8 @@ function updateClaudeJsonConfig(configPath) {
402
406
  env: {
403
407
  MCP_SERVER_URL: "https://mcp.i18nagent.ai",
404
408
  API_KEY: existingApiKey || ""
405
- }
409
+ },
410
+ disabled: false
406
411
  };
407
412
  } else {
408
413
  config.mcpServers["i18n-agent"] = {
@@ -411,7 +416,8 @@ function updateClaudeJsonConfig(configPath) {
411
416
  env: {
412
417
  MCP_SERVER_URL: "https://mcp.i18nagent.ai",
413
418
  API_KEY: existingApiKey || ""
414
- }
419
+ },
420
+ disabled: false
415
421
  };
416
422
  }
417
423
 
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.17';
8
+ const MCP_CLIENT_VERSION = '1.8.19';
9
9
 
10
10
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
11
11
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
@@ -47,7 +47,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
47
47
  tools: [
48
48
  {
49
49
  name: 'translate_text',
50
- 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.',
50
+ 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.',
51
51
  inputSchema: {
52
52
  type: 'object',
53
53
  properties: {
@@ -83,6 +83,36 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
83
83
  type: 'string',
84
84
  description: 'Optional additional context or instructions for the translation (e.g., "Keep technical terms in English", "Use formal tone")',
85
85
  },
86
+ pseudoTranslation: {
87
+ type: 'boolean',
88
+ description: 'Enable pseudo-translation mode for testing i18n implementations (bypasses AI translation, no credit cost)',
89
+ },
90
+ pseudoOptions: {
91
+ type: 'object',
92
+ properties: {
93
+ addCJK: {
94
+ type: 'boolean',
95
+ description: 'Add CJK characters to test wide character support',
96
+ },
97
+ expansionRatio: {
98
+ type: 'number',
99
+ description: 'Length expansion ratio (1.0 = no expansion, 1.3 = 30% longer, 2.0 = double length)',
100
+ },
101
+ addSpecialChars: {
102
+ type: 'boolean',
103
+ description: 'Add special characters to test encoding/escaping',
104
+ },
105
+ addBrackets: {
106
+ type: 'boolean',
107
+ description: 'Wrap strings with brackets to identify untranslated content',
108
+ },
109
+ addAccents: {
110
+ type: 'boolean',
111
+ description: 'Replace Latin characters with accented equivalents',
112
+ },
113
+ },
114
+ description: 'Configuration options for pseudo-translation',
115
+ },
86
116
  },
87
117
  required: ['texts', 'targetLanguages'],
88
118
  },
@@ -103,7 +133,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
103
133
  },
104
134
  {
105
135
  name: 'translate_file',
106
- 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.',
136
+ 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.',
107
137
  inputSchema: {
108
138
  type: 'object',
109
139
  properties: {
@@ -160,6 +190,36 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
160
190
  type: 'string',
161
191
  description: 'Optional additional context or instructions for the translation (e.g., "Keep technical terms in English", "Use formal tone")',
162
192
  },
193
+ pseudoTranslation: {
194
+ type: 'boolean',
195
+ description: 'Enable pseudo-translation mode for testing i18n implementations (bypasses AI translation, no credit cost)',
196
+ },
197
+ pseudoOptions: {
198
+ type: 'object',
199
+ properties: {
200
+ addCJK: {
201
+ type: 'boolean',
202
+ description: 'Add CJK characters to test wide character support',
203
+ },
204
+ expansionRatio: {
205
+ type: 'number',
206
+ description: 'Length expansion ratio (1.0 = no expansion, 1.3 = 30% longer, 2.0 = double length)',
207
+ },
208
+ addSpecialChars: {
209
+ type: 'boolean',
210
+ description: 'Add special characters to test encoding/escaping',
211
+ },
212
+ addBrackets: {
213
+ type: 'boolean',
214
+ description: 'Wrap strings with brackets to identify untranslated content',
215
+ },
216
+ addAccents: {
217
+ type: 'boolean',
218
+ description: 'Replace Latin characters with accented equivalents',
219
+ },
220
+ },
221
+ description: 'Configuration options for pseudo-translation',
222
+ },
163
223
  },
164
224
  required: ['targetLanguages'],
165
225
  },
@@ -376,7 +436,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
376
436
  });
377
437
 
378
438
  async function handleTranslateText(args) {
379
- const { texts, targetLanguages: rawTargetLanguages, sourceLanguage, targetAudience = 'general', industry = 'technology', region, context } = args;
439
+ const { texts, targetLanguages: rawTargetLanguages, sourceLanguage, targetAudience = 'general', industry = 'technology', region, context, pseudoTranslation, pseudoOptions } = args;
380
440
 
381
441
  if (!texts || !Array.isArray(texts) || texts.length === 0) {
382
442
  throw new Error('texts must be a non-empty array');
@@ -420,6 +480,8 @@ async function handleTranslateText(args) {
420
480
  industry: industry,
421
481
  region: region,
422
482
  context: context,
483
+ pseudoTranslation: pseudoTranslation,
484
+ pseudoOptions: pseudoOptions,
423
485
  }
424
486
  }
425
487
  };
@@ -684,7 +746,9 @@ async function handleTranslateFile(args) {
684
746
  outputFormat = 'same',
685
747
  sourceLanguage,
686
748
  region,
687
- context
749
+ context,
750
+ pseudoTranslation,
751
+ pseudoOptions
688
752
  } = args;
689
753
 
690
754
  if (!filePath && !fileContent) {
@@ -740,6 +804,8 @@ async function handleTranslateFile(args) {
740
804
  if (targetLanguages !== undefined) requestArgs.targetLanguages = targetLanguages;
741
805
  if (region !== undefined) requestArgs.region = region;
742
806
  if (context !== undefined) requestArgs.context = context;
807
+ if (pseudoTranslation !== undefined) requestArgs.pseudoTranslation = pseudoTranslation;
808
+ if (pseudoOptions !== undefined) requestArgs.pseudoOptions = pseudoOptions;
743
809
 
744
810
  // Use MCP JSON-RPC protocol for translate_file
745
811
  const mcpRequest = {
@@ -1630,11 +1696,8 @@ async function handleDownloadTranslations(args) {
1630
1696
  parsedResult = result;
1631
1697
  }
1632
1698
 
1633
- if (!parsedResult.success) {
1634
- throw new Error(parsedResult.error || 'Failed to get download URLs');
1635
- }
1636
-
1637
- // Step 2: Download files from URLs and write to local /tmp
1699
+ // Detect storage type and handle accordingly
1700
+ const storageType = parsedResult.storageType || 'local';
1638
1701
  const outputDir = `/tmp/i18n-translations-${jobId}`;
1639
1702
 
1640
1703
  // Create output directory
@@ -1642,40 +1705,65 @@ async function handleDownloadTranslations(args) {
1642
1705
  fs.mkdirSync(outputDir, { recursive: true });
1643
1706
  }
1644
1707
 
1645
- const downloadUrls = parsedResult.downloadUrls;
1646
- if (!downloadUrls || downloadUrls.length === 0) {
1647
- throw new Error('No download URLs provided by server');
1648
- }
1649
-
1650
1708
  const filesWritten = [];
1651
1709
 
1652
- // Download each language file
1653
- for (const { language, url } of downloadUrls) {
1654
- try {
1655
- console.error(`📥 Downloading ${language}...`);
1710
+ if (storageType === 's3' && parsedResult.downloadUrls) {
1711
+ // Case 1: S3 Storage - download files from presigned URLs
1712
+ console.error(`📥 Downloading ${Object.keys(parsedResult.downloadUrls).length} translation files from S3...`);
1656
1713
 
1657
- const fileResponse = await axios.get(url, {
1658
- responseType: 'text',
1659
- timeout: 60000, // 1 minute per file
1660
- headers: {
1661
- 'Authorization': `Bearer ${API_KEY}`
1662
- }
1663
- });
1714
+ for (const [language, urlInfo] of Object.entries(parsedResult.downloadUrls)) {
1715
+ try {
1716
+ console.error(`📥 Downloading ${language}...`);
1717
+
1718
+ const fileResponse = await axios.get(urlInfo.url, {
1719
+ responseType: 'text',
1720
+ timeout: 60000, // 1 minute per file
1721
+ headers: {
1722
+ 'Authorization': `Bearer ${API_KEY}`
1723
+ }
1724
+ });
1664
1725
 
1665
- // Determine file extension from metadata
1666
- const fileType = parsedResult.metadata?.fileType || 'json';
1667
- const fileName = `${language}.${fileType}`;
1668
- const filePath = path.join(outputDir, fileName);
1726
+ // Determine file extension from file name or metadata
1727
+ const fileType = parsedResult.fileName?.split('.').pop() || 'json';
1728
+ const fileName = `${language}.${fileType}`;
1729
+ const filePath = path.join(outputDir, fileName);
1669
1730
 
1670
- // Write file to disk
1671
- fs.writeFileSync(filePath, fileResponse.data, 'utf8');
1672
- filesWritten.push(filePath);
1731
+ // Write file to disk
1732
+ fs.writeFileSync(filePath, fileResponse.data, 'utf8');
1733
+ filesWritten.push(filePath);
1673
1734
 
1674
- console.error(`✅ Downloaded ${fileName}`);
1675
- } catch (downloadError) {
1676
- console.error(`❌ Failed to download ${language}:`, downloadError.message);
1677
- throw new Error(`Failed to download ${language}: ${downloadError.message}`);
1735
+ console.error(`✅ Downloaded ${fileName}`);
1736
+ } catch (downloadError) {
1737
+ console.error(`❌ Failed to download ${language}:`, downloadError.message);
1738
+ throw new Error(`Failed to download ${language}: ${downloadError.message}`);
1739
+ }
1678
1740
  }
1741
+ } else if (parsedResult.translations) {
1742
+ // Case 2: Raw Translations - write directly from response
1743
+ console.error(`💾 Writing ${Object.keys(parsedResult.translations).length} translation files from raw content...`);
1744
+
1745
+ for (const [language, content] of Object.entries(parsedResult.translations)) {
1746
+ try {
1747
+ console.error(`💾 Writing ${language}...`);
1748
+
1749
+ // Determine file extension from file name or default to json
1750
+ const fileType = parsedResult.fileName?.split('.').pop() || 'json';
1751
+ const fileName = `${language}.${fileType}`;
1752
+ const filePath = path.join(outputDir, fileName);
1753
+
1754
+ // Write file to disk
1755
+ fs.writeFileSync(filePath, content, 'utf8');
1756
+ filesWritten.push(filePath);
1757
+
1758
+ console.error(`✅ Wrote ${fileName}`);
1759
+ } catch (writeError) {
1760
+ console.error(`❌ Failed to write ${language}:`, writeError.message);
1761
+ throw new Error(`Failed to write ${language}: ${writeError.message}`);
1762
+ }
1763
+ }
1764
+ } else {
1765
+ // No valid download method found
1766
+ throw new Error(`No translations available. Storage type: ${storageType}. Expected either downloadUrls (S3) or translations (raw content).`);
1679
1767
  }
1680
1768
 
1681
1769
  // Return success with file paths
@@ -1687,8 +1775,10 @@ async function handleDownloadTranslations(args) {
1687
1775
  jobId,
1688
1776
  outputDirectory: outputDir,
1689
1777
  filesWritten,
1690
- metadata: parsedResult.metadata,
1691
- message: `✅ Downloaded ${filesWritten.length} translation files to ${outputDir}`
1778
+ storageType,
1779
+ fileName: parsedResult.fileName,
1780
+ targetLanguages: parsedResult.targetLanguages,
1781
+ message: `✅ ${storageType === 's3' ? 'Downloaded' : 'Wrote'} ${filesWritten.length} translation files to ${outputDir}`
1692
1782
  }, null, 2)
1693
1783
  }]
1694
1784
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@i18n-agent/mcp-client",
3
- "version": "1.8.17",
3
+ "version": "1.8.19",
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": {