@i18n-agent/mcp-client 1.1.2 → 1.1.3
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.
- package/mcp-client.js +72 -35
- package/package.json +1 -1
package/mcp-client.js
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* Integrates with Claude Code CLI to provide translation capabilities
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
const MCP_CLIENT_VERSION = '1.1.2';
|
|
9
|
+
|
|
8
10
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
9
11
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
10
12
|
import {
|
|
@@ -17,8 +19,6 @@ import axios from 'axios';
|
|
|
17
19
|
import fs from 'fs';
|
|
18
20
|
import path from 'path';
|
|
19
21
|
|
|
20
|
-
const MCP_CLIENT_VERSION = '1.1.2';
|
|
21
|
-
|
|
22
22
|
const server = new Server(
|
|
23
23
|
{
|
|
24
24
|
name: 'i18n-agent',
|
|
@@ -243,7 +243,6 @@ async function handleTranslateText(args) {
|
|
|
243
243
|
};
|
|
244
244
|
|
|
245
245
|
try {
|
|
246
|
-
console.error(`[MCP v${MCP_CLIENT_VERSION}/STDIO/translate_text] Request: ${texts.length} texts, ${totalChars} chars to ${MCP_SERVER_URL}`);
|
|
247
246
|
const response = await axios.post(MCP_SERVER_URL, mcpRequest, {
|
|
248
247
|
headers: {
|
|
249
248
|
'Content-Type': 'application/json',
|
|
@@ -308,30 +307,47 @@ async function handleTranslateText(args) {
|
|
|
308
307
|
};
|
|
309
308
|
}
|
|
310
309
|
|
|
311
|
-
//
|
|
310
|
+
// Handle 401 unauthorized - invalid API key
|
|
311
|
+
if (error.response?.status === 401) {
|
|
312
|
+
const errorDetails = error.response.data?.message || error.response.data?.result?.content?.[0]?.text || error.message;
|
|
313
|
+
throw new Error(`❌ Invalid API key (401)\nDetails: ${errorDetails}\nPlease check your API key at https://app.i18nagent.ai\n[MCP v${MCP_CLIENT_VERSION}/STDIO/translate_text]`);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Handle 402 payment required with user-friendly message
|
|
312
317
|
if (error.response?.status === 402) {
|
|
313
|
-
|
|
314
|
-
throw new Error(`⚠️ Insufficient credits
|
|
318
|
+
const errorDetails = error.response.data?.message || error.response.data?.result?.content?.[0]?.text || error.message;
|
|
319
|
+
throw new Error(`⚠️ Insufficient credits (402)\nDetails: ${errorDetails}\nPlease top up at https://app.i18nagent.ai\n[MCP v${MCP_CLIENT_VERSION}/STDIO/translate_text]`);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Check if it's a large content issue
|
|
323
|
+
const totalChars = texts.reduce((sum, text) => sum + text.length, 0);
|
|
324
|
+
if (error.response?.status === 413 ||
|
|
325
|
+
(error.response?.status === 503 && totalChars > 50000)) {
|
|
326
|
+
const errorDetails = error.response?.data?.message || error.response?.data?.result?.content?.[0]?.text || error.message;
|
|
327
|
+
const errorMsg = `Content too large (${totalChars} characters, ${texts.length} texts)\nStatus: ${error.response?.status}\nDetails: ${errorDetails}\n\nPlease break into smaller batches:\n• Split into batches of 50-100 texts\n• Keep total size under 50KB per request\n• Process sequentially to avoid overload\n[MCP v${MCP_CLIENT_VERSION}/STDIO/translate_text]`;
|
|
328
|
+
throw new Error(errorMsg);
|
|
315
329
|
}
|
|
316
330
|
|
|
317
|
-
// Check if it's actually a service unavailable error (
|
|
331
|
+
// Check if it's actually a service unavailable error (only for real infrastructure issues)
|
|
318
332
|
if (error.code === 'ECONNREFUSED' ||
|
|
319
333
|
error.code === 'ETIMEDOUT' ||
|
|
320
|
-
error.
|
|
334
|
+
error.code === 'ENOTFOUND' ||
|
|
335
|
+
(error.response?.status === 503 && totalChars <= 50000) ||
|
|
321
336
|
error.response?.status === 502 ||
|
|
322
337
|
error.response?.status === 504) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
});
|
|
329
|
-
throw new Error(`Translation service unavailable [MCP v${MCP_CLIENT_VERSION}/STDIO/translate_text]: ${error.message}`);
|
|
338
|
+
const errorDetails = error.response?.data?.result?.content?.[0]?.text ||
|
|
339
|
+
error.response?.data?.error?.message ||
|
|
340
|
+
error.message;
|
|
341
|
+
const debugInfo = `Code: ${error.code || 'N/A'}\nStatus: ${error.response?.status || 'N/A'}\nStatusText: ${error.response?.statusText || 'N/A'}\nDetails: ${errorDetails}\nURL: ${error.config?.url || 'N/A'}\nTimestamp: ${new Date().toISOString()}`;
|
|
342
|
+
throw new Error(`Translation service error\n${debugInfo}\n[MCP v${MCP_CLIENT_VERSION}/STDIO/translate_text]`);
|
|
330
343
|
}
|
|
331
344
|
|
|
332
|
-
// For other errors
|
|
333
|
-
|
|
334
|
-
|
|
345
|
+
// For other errors, include all debug info in the error message
|
|
346
|
+
const errorDetails = error.response?.data?.result?.content?.[0]?.text ||
|
|
347
|
+
error.response?.data?.error?.message ||
|
|
348
|
+
error.message;
|
|
349
|
+
const debugInfo = `Status: ${error.response?.status || 'N/A'}\nStatusText: ${error.response?.statusText || 'N/A'}\nDetails: ${errorDetails}\nTimestamp: ${new Date().toISOString()}`;
|
|
350
|
+
throw new Error(`Error\n${debugInfo}\n[MCP v${MCP_CLIENT_VERSION}/STDIO/translate_text]`);
|
|
335
351
|
}
|
|
336
352
|
}
|
|
337
353
|
|
|
@@ -469,7 +485,6 @@ async function handleTranslateFile(args) {
|
|
|
469
485
|
};
|
|
470
486
|
|
|
471
487
|
try {
|
|
472
|
-
console.error(`[MCP v${MCP_CLIENT_VERSION}/STDIO/translate_file] Request: ${content?.length || 0} chars to ${MCP_SERVER_URL}`);
|
|
473
488
|
const response = await axios.post(MCP_SERVER_URL, mcpRequest, {
|
|
474
489
|
headers: {
|
|
475
490
|
'Content-Type': 'application/json',
|
|
@@ -502,6 +517,8 @@ async function handleTranslateFile(args) {
|
|
|
502
517
|
return result;
|
|
503
518
|
|
|
504
519
|
} catch (error) {
|
|
520
|
+
// Debug info will be included in error messages for visibility
|
|
521
|
+
|
|
505
522
|
if (error.code === 'ECONNABORTED') {
|
|
506
523
|
return {
|
|
507
524
|
content: [
|
|
@@ -524,30 +541,50 @@ async function handleTranslateFile(args) {
|
|
|
524
541
|
};
|
|
525
542
|
}
|
|
526
543
|
|
|
527
|
-
//
|
|
544
|
+
// Handle 401 unauthorized - invalid API key
|
|
545
|
+
if (error.response?.status === 401) {
|
|
546
|
+
const errorDetails = error.response.data?.message || error.response.data?.result?.content?.[0]?.text || error.message;
|
|
547
|
+
throw new Error(`❌ Invalid API key (401)\nDetails: ${errorDetails}\nPlease check your API key at https://app.i18nagent.ai\n[MCP v${MCP_CLIENT_VERSION}/STDIO/translate_file]`);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// Handle 402 payment required with user-friendly message
|
|
528
551
|
if (error.response?.status === 402) {
|
|
529
|
-
|
|
530
|
-
throw new Error(`⚠️ Insufficient credits
|
|
552
|
+
const errorDetails = error.response.data?.message || error.response.data?.result?.content?.[0]?.text || error.message;
|
|
553
|
+
throw new Error(`⚠️ Insufficient credits (402)\nDetails: ${errorDetails}\nPlease top up at https://app.i18nagent.ai\n[MCP v${MCP_CLIENT_VERSION}/STDIO/translate_file]`);
|
|
531
554
|
}
|
|
532
555
|
|
|
533
|
-
// Check if it's
|
|
556
|
+
// Check if it's a timeout issue (45-second server timeout) or large file issue
|
|
557
|
+
const errorDetails = error.response?.data?.result?.content?.[0]?.text ||
|
|
558
|
+
error.response?.data?.error?.message ||
|
|
559
|
+
error.message;
|
|
560
|
+
|
|
561
|
+
if (error.response?.status === 413 ||
|
|
562
|
+
(error.response?.status === 503 && content.length > 50000) ||
|
|
563
|
+
(error.response?.status === 503 && errorDetails.includes('timeout after 45 seconds'))) {
|
|
564
|
+
const errorMsg = `File too large or complex (${content.length} characters)\n\nThe server has a 45-second timeout. Your file requires more processing time.\n\nPlease break into smaller chunks:\n• Split files over 50KB into multiple parts\n• Translate sections separately (e.g., split by top-level keys for JSON)\n• Use translate_text for batches of 50-100 strings\n• Each chunk should process in under 45 seconds\n\nAlternatively, wait for async job support (coming soon).\n[MCP v${MCP_CLIENT_VERSION}/STDIO/translate_file]`;
|
|
565
|
+
throw new Error(errorMsg);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Check if it's actually a service unavailable error (only for real infrastructure issues)
|
|
534
569
|
if (error.code === 'ECONNREFUSED' ||
|
|
535
570
|
error.code === 'ETIMEDOUT' ||
|
|
536
|
-
error.
|
|
571
|
+
error.code === 'ENOTFOUND' ||
|
|
572
|
+
(error.response?.status === 503 && content.length <= 50000) ||
|
|
537
573
|
error.response?.status === 502 ||
|
|
538
574
|
error.response?.status === 504) {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
});
|
|
545
|
-
throw new Error(`Translation service unavailable [MCP v${MCP_CLIENT_VERSION}/STDIO/translate_file]: ${error.message}`);
|
|
575
|
+
const errorDetails = error.response?.data?.result?.content?.[0]?.text ||
|
|
576
|
+
error.response?.data?.error?.message ||
|
|
577
|
+
error.message;
|
|
578
|
+
const debugInfo = `Code: ${error.code || 'N/A'}\nStatus: ${error.response?.status || 'N/A'}\nStatusText: ${error.response?.statusText || 'N/A'}\nDetails: ${errorDetails}\nURL: ${error.config?.url || 'N/A'}\nTimestamp: ${new Date().toISOString()}`;
|
|
579
|
+
throw new Error(`Translation service error\n${debugInfo}\n[MCP v${MCP_CLIENT_VERSION}/STDIO/translate_file]`);
|
|
546
580
|
}
|
|
547
581
|
|
|
548
|
-
// For other errors,
|
|
549
|
-
|
|
550
|
-
|
|
582
|
+
// For other errors, include all debug info in the error message
|
|
583
|
+
const errorDetails = error.response?.data?.result?.content?.[0]?.text ||
|
|
584
|
+
error.response?.data?.error?.message ||
|
|
585
|
+
error.message;
|
|
586
|
+
const debugInfo = `Status: ${error.response?.status || 'N/A'}\nStatusText: ${error.response?.statusText || 'N/A'}\nDetails: ${errorDetails}\nTimestamp: ${new Date().toISOString()}`;
|
|
587
|
+
throw new Error(`Error\n${debugInfo}\n[MCP v${MCP_CLIENT_VERSION}/STDIO/translate_file]`);
|
|
551
588
|
}
|
|
552
589
|
}
|
|
553
590
|
|
|
@@ -627,7 +664,7 @@ async function pollTranslationJob(jobId, estimatedTime) {
|
|
|
627
664
|
async function handleGetCredits(args) {
|
|
628
665
|
try {
|
|
629
666
|
// Get team info first using the API key
|
|
630
|
-
const teamResponse = await axios.get(`https://
|
|
667
|
+
const teamResponse = await axios.get(`https://app.i18nagent.ai/api/teams/by-api-key/${API_KEY}`, {
|
|
631
668
|
headers: {
|
|
632
669
|
'Content-Type': 'application/json'
|
|
633
670
|
},
|
package/package.json
CHANGED