@i18n-agent/mcp-client 1.0.3 → 1.1.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.
- package/README.md +13 -0
- package/mcp-client.js +246 -88
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -9,6 +9,9 @@ Professional translation service client for Claude, Cursor, VS Code, and other A
|
|
|
9
9
|
|
|
10
10
|
- **🎯 Smart Translation**: Context-aware translations with cultural adaptation
|
|
11
11
|
- **📁 File Translation**: Support for JSON, YAML, CSV, XML, Markdown, and more
|
|
12
|
+
- **⚡ Large File Support**: Async processing for files >50KB with progress tracking
|
|
13
|
+
- **🔄 Timeout Improvements**: Extended timeouts (5-10 min) for large translations
|
|
14
|
+
- **📊 Progress Tracking**: Real-time job status and completion monitoring
|
|
12
15
|
- **💰 Credit Tracking**: Real-time credit balance and word count estimates
|
|
13
16
|
- **🌐 30+ Languages**: Multi-tier language support with quality ratings
|
|
14
17
|
- **🔧 Easy Setup**: One-command installation for major AI IDEs
|
|
@@ -180,6 +183,16 @@ Create `.cursor/mcp_settings.json` or `.vscode/mcp_settings.json`:
|
|
|
180
183
|
- **Preserve Structure**: Keeps original file format and structure
|
|
181
184
|
- **Output Format**: Convert between formats (JSON ↔ YAML ↔ CSV)
|
|
182
185
|
- **Large Files**: Automatically chunks large files for processing
|
|
186
|
+
- **Async Processing**: Files >50KB processed asynchronously with job tracking
|
|
187
|
+
- **Progress Monitoring**: Real-time status updates for long-running translations
|
|
188
|
+
- **Timeout Resilience**: Up to 10 minutes for large translation jobs
|
|
189
|
+
|
|
190
|
+
### Large Translation Handling
|
|
191
|
+
- **Async Processing**: >100 texts or >50KB files processed asynchronously
|
|
192
|
+
- **Job Tracking**: Unique job IDs for monitoring long-running translations
|
|
193
|
+
- **Progress Updates**: Real-time completion percentages and status
|
|
194
|
+
- **Extended Timeouts**: 5-10 minute timeouts prevent interruptions
|
|
195
|
+
- **Automatic Polling**: Client automatically polls for job completion
|
|
183
196
|
|
|
184
197
|
### Credit Management
|
|
185
198
|
- **Cost**: 0.001 credits per word
|
package/mcp-client.js
CHANGED
|
@@ -30,16 +30,14 @@ const server = new Server(
|
|
|
30
30
|
);
|
|
31
31
|
|
|
32
32
|
// Configuration
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
// Validate required environment variables
|
|
37
|
-
if (!API_KEY) {
|
|
38
|
-
console.error('❌ Error: API_KEY environment variable is required');
|
|
39
|
-
console.error('💡 Get your API key from: https://app.i18nagent.ai');
|
|
40
|
-
console.error('💡 Set it with: export API_KEY=your-api-key-here');
|
|
41
|
-
process.exit(1);
|
|
33
|
+
if (!process.env.MCP_SERVER_URL) {
|
|
34
|
+
throw new Error('MCP_SERVER_URL environment variable is required');
|
|
42
35
|
}
|
|
36
|
+
if (!process.env.API_KEY) {
|
|
37
|
+
throw new Error('API_KEY environment variable is required');
|
|
38
|
+
}
|
|
39
|
+
const MCP_SERVER_URL = process.env.MCP_SERVER_URL;
|
|
40
|
+
const API_KEY = process.env.API_KEY;
|
|
43
41
|
|
|
44
42
|
// Available tools
|
|
45
43
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
@@ -208,7 +206,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
208
206
|
});
|
|
209
207
|
|
|
210
208
|
async function handleTranslateText(args) {
|
|
211
|
-
const { texts, targetLanguage, sourceLanguage, targetAudience = 'general', industry = 'technology', region } = args;
|
|
209
|
+
const { texts, targetLanguage, sourceLanguage, targetAudience = 'general', industry = 'technology', region, notes } = args;
|
|
212
210
|
|
|
213
211
|
if (!texts || !Array.isArray(texts) || texts.length === 0) {
|
|
214
212
|
throw new Error('texts must be a non-empty array');
|
|
@@ -218,102 +216,144 @@ async function handleTranslateText(args) {
|
|
|
218
216
|
throw new Error('targetLanguage is required');
|
|
219
217
|
}
|
|
220
218
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
219
|
+
// Check if this is a large translation request
|
|
220
|
+
const totalChars = texts.reduce((sum, text) => sum + text.length, 0);
|
|
221
|
+
const isLargeRequest = texts.length > 100 || totalChars > 50000;
|
|
222
|
+
|
|
223
|
+
// Use MCP JSON-RPC protocol for translate_content
|
|
224
|
+
const mcpRequest = {
|
|
225
|
+
jsonrpc: '2.0',
|
|
226
|
+
id: Date.now(),
|
|
227
|
+
method: 'tools/call',
|
|
228
|
+
params: {
|
|
229
|
+
name: 'translate_content',
|
|
230
|
+
arguments: {
|
|
231
|
+
apiKey: API_KEY,
|
|
232
|
+
texts: texts,
|
|
233
|
+
targetLanguage: targetLanguage,
|
|
234
|
+
sourceLanguage: sourceLanguage && sourceLanguage !== 'auto' ? sourceLanguage : undefined,
|
|
235
|
+
targetAudience: targetAudience,
|
|
236
|
+
industry: industry,
|
|
237
|
+
region: region,
|
|
238
|
+
notes: notes,
|
|
239
|
+
}
|
|
240
|
+
}
|
|
229
241
|
};
|
|
230
242
|
|
|
231
243
|
try {
|
|
232
|
-
const response = await axios.post(
|
|
244
|
+
const response = await axios.post(MCP_SERVER_URL, mcpRequest, {
|
|
233
245
|
headers: {
|
|
234
246
|
'Content-Type': 'application/json',
|
|
235
247
|
},
|
|
236
|
-
timeout:
|
|
248
|
+
timeout: isLargeRequest ? 600000 : 300000, // 10 minutes for large requests, 5 minutes for normal
|
|
237
249
|
});
|
|
238
250
|
|
|
239
251
|
if (response.data.error) {
|
|
240
|
-
throw new Error(`Translation service error: ${response.data.error}`);
|
|
252
|
+
throw new Error(`Translation service error: ${response.data.error.message || response.data.error}`);
|
|
241
253
|
}
|
|
242
254
|
|
|
243
|
-
//
|
|
244
|
-
const
|
|
255
|
+
// Check if we got an async job response
|
|
256
|
+
const result = response.data.result;
|
|
245
257
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
|
|
258
|
+
if (result && result.content && result.content[0]) {
|
|
259
|
+
const textContent = result.content[0].text;
|
|
260
|
+
|
|
261
|
+
// Try to parse as JSON to check for job ID
|
|
262
|
+
try {
|
|
263
|
+
const parsed = JSON.parse(textContent);
|
|
264
|
+
if (parsed.status === 'processing' && parsed.jobId) {
|
|
265
|
+
// Async job started - poll for status
|
|
266
|
+
const jobResult = await pollTranslationJob(parsed.jobId, parsed.estimatedTime);
|
|
267
|
+
|
|
268
|
+
// Extract the actual translation result from the job result
|
|
269
|
+
if (jobResult && jobResult.content && jobResult.content[0]) {
|
|
270
|
+
const translationData = JSON.parse(jobResult.content[0].text);
|
|
271
|
+
return formatTranslationResult(translationData, texts, targetLanguage, sourceLanguage, targetAudience, industry, region);
|
|
272
|
+
}
|
|
273
|
+
return jobResult;
|
|
274
|
+
} else {
|
|
275
|
+
// Regular synchronous result
|
|
276
|
+
return formatTranslationResult(parsed, texts, targetLanguage, sourceLanguage, targetAudience, industry, region);
|
|
277
|
+
}
|
|
278
|
+
} catch {
|
|
279
|
+
// Not JSON or error parsing - return as-is
|
|
280
|
+
return result;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return result;
|
|
265
285
|
} catch (error) {
|
|
266
286
|
if (error.code === 'ECONNABORTED') {
|
|
267
|
-
|
|
287
|
+
return {
|
|
288
|
+
content: [
|
|
289
|
+
{
|
|
290
|
+
type: 'text',
|
|
291
|
+
text: `⚠️ Translation Timeout\n\n` +
|
|
292
|
+
`The translation is taking longer than expected.\n` +
|
|
293
|
+
`This is normal for requests with 100+ texts or over 50KB of content.\n\n` +
|
|
294
|
+
`What's happening:\n` +
|
|
295
|
+
`• The translation is still processing on the server\n` +
|
|
296
|
+
`• Large requests are processed with optimized pipeline\n` +
|
|
297
|
+
`• Each batch ensures quality and consistency\n\n` +
|
|
298
|
+
`Recommendations:\n` +
|
|
299
|
+
`1. Try splitting into smaller batches (50-100 texts)\n` +
|
|
300
|
+
`2. Use shorter texts when possible\n` +
|
|
301
|
+
`3. Contact support if this persists\n\n` +
|
|
302
|
+
`Request size: ${texts.length} texts, ${totalChars} characters`
|
|
303
|
+
}
|
|
304
|
+
]
|
|
305
|
+
};
|
|
268
306
|
}
|
|
269
|
-
|
|
307
|
+
|
|
308
|
+
// Check if it's actually a service unavailable error (503, timeout, connection issues)
|
|
309
|
+
if (error.code === 'ECONNREFUSED' ||
|
|
310
|
+
error.code === 'ETIMEDOUT' ||
|
|
311
|
+
error.response?.status === 503 ||
|
|
312
|
+
error.response?.status === 502 ||
|
|
313
|
+
error.response?.status === 504) {
|
|
314
|
+
throw new Error(`Translation service unavailable: ${error.message}`);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// For other errors (401, 402, 404, etc), throw them as-is without "unavailable" keyword
|
|
318
|
+
throw error;
|
|
270
319
|
}
|
|
271
320
|
}
|
|
272
321
|
|
|
273
322
|
async function handleListLanguages(args) {
|
|
274
323
|
const { includeQuality = true } = args;
|
|
275
324
|
|
|
276
|
-
// Language support matrix based on
|
|
325
|
+
// Language support matrix based on GPT-OSS analysis
|
|
277
326
|
const languages = {
|
|
278
|
-
'Tier 1 - Excellent Quality': {
|
|
327
|
+
'Tier 1 - Production Ready (Excellent Quality 80-90%)': {
|
|
279
328
|
'en': 'English',
|
|
329
|
+
'es': 'Spanish',
|
|
280
330
|
'fr': 'French',
|
|
281
331
|
'de': 'German',
|
|
282
|
-
'es': 'Spanish',
|
|
283
332
|
'it': 'Italian',
|
|
284
333
|
'pt': 'Portuguese',
|
|
334
|
+
'nl': 'Dutch',
|
|
335
|
+
},
|
|
336
|
+
'Tier 2 - Production Viable (Good Quality 50-75%)': {
|
|
285
337
|
'ru': 'Russian',
|
|
338
|
+
'zh-CN': 'Chinese (Simplified)',
|
|
286
339
|
'ja': 'Japanese',
|
|
287
340
|
'ko': 'Korean',
|
|
288
|
-
'zh-CN': 'Chinese (Simplified)',
|
|
289
|
-
},
|
|
290
|
-
'Tier 2 - High Quality': {
|
|
291
|
-
'nl': 'Dutch',
|
|
292
|
-
'pl': 'Polish',
|
|
293
|
-
'cs': 'Czech',
|
|
294
341
|
'ar': 'Arabic',
|
|
295
342
|
'he': 'Hebrew',
|
|
296
343
|
'hi': 'Hindi',
|
|
344
|
+
'pl': 'Polish',
|
|
345
|
+
'cs': 'Czech',
|
|
346
|
+
},
|
|
347
|
+
'Tier 3 - Basic Support (Use with Caution 20-50%)': {
|
|
297
348
|
'zh-TW': 'Chinese (Traditional)',
|
|
349
|
+
'th': 'Thai',
|
|
350
|
+
'vi': 'Vietnamese',
|
|
298
351
|
'sv': 'Swedish',
|
|
299
352
|
'da': 'Danish',
|
|
300
353
|
'no': 'Norwegian',
|
|
301
354
|
'fi': 'Finnish',
|
|
302
|
-
},
|
|
303
|
-
'Tier 3 - Good Quality': {
|
|
304
355
|
'tr': 'Turkish',
|
|
305
356
|
'hu': 'Hungarian',
|
|
306
|
-
'th': 'Thai',
|
|
307
|
-
'vi': 'Vietnamese',
|
|
308
|
-
'uk': 'Ukrainian',
|
|
309
|
-
'bg': 'Bulgarian',
|
|
310
|
-
'ro': 'Romanian',
|
|
311
|
-
'hr': 'Croatian',
|
|
312
|
-
'sk': 'Slovak',
|
|
313
|
-
'sl': 'Slovenian',
|
|
314
|
-
'et': 'Estonian',
|
|
315
|
-
'lv': 'Latvian',
|
|
316
|
-
'lt': 'Lithuanian',
|
|
317
357
|
},
|
|
318
358
|
};
|
|
319
359
|
|
|
@@ -362,7 +402,8 @@ async function handleTranslateFile(args) {
|
|
|
362
402
|
preserveKeys = true,
|
|
363
403
|
outputFormat = 'same',
|
|
364
404
|
sourceLanguage,
|
|
365
|
-
region
|
|
405
|
+
region,
|
|
406
|
+
notes
|
|
366
407
|
} = args;
|
|
367
408
|
|
|
368
409
|
if (!filePath && !fileContent) {
|
|
@@ -384,6 +425,9 @@ async function handleTranslateFile(args) {
|
|
|
384
425
|
}
|
|
385
426
|
}
|
|
386
427
|
|
|
428
|
+
// Check if this is a large file that might need async processing
|
|
429
|
+
const isLargeFile = content.length > 50000; // > 50KB
|
|
430
|
+
|
|
387
431
|
// Use MCP JSON-RPC protocol for translate_file
|
|
388
432
|
const mcpRequest = {
|
|
389
433
|
jsonrpc: '2.0',
|
|
@@ -401,6 +445,7 @@ async function handleTranslateFile(args) {
|
|
|
401
445
|
targetAudience,
|
|
402
446
|
industry,
|
|
403
447
|
region,
|
|
448
|
+
notes,
|
|
404
449
|
preserveKeys,
|
|
405
450
|
outputFormat
|
|
406
451
|
}
|
|
@@ -412,46 +457,159 @@ async function handleTranslateFile(args) {
|
|
|
412
457
|
headers: {
|
|
413
458
|
'Content-Type': 'application/json',
|
|
414
459
|
},
|
|
415
|
-
timeout:
|
|
460
|
+
timeout: isLargeFile ? 600000 : 300000, // 10 minutes for large files, 5 minutes for normal
|
|
416
461
|
});
|
|
417
462
|
|
|
418
463
|
if (response.data.error) {
|
|
419
464
|
throw new Error(`Translation service error: ${response.data.error.message || response.data.error}`);
|
|
420
465
|
}
|
|
421
466
|
|
|
422
|
-
//
|
|
467
|
+
// Check if we got an async job response
|
|
423
468
|
const result = response.data.result;
|
|
469
|
+
|
|
470
|
+
if (result && result.content && result.content[0]) {
|
|
471
|
+
const textContent = result.content[0].text;
|
|
472
|
+
|
|
473
|
+
// Try to parse as JSON to check for job ID
|
|
474
|
+
try {
|
|
475
|
+
const parsed = JSON.parse(textContent);
|
|
476
|
+
if (parsed.status === 'processing' && parsed.jobId) {
|
|
477
|
+
// Async job started - poll for status
|
|
478
|
+
return await pollTranslationJob(parsed.jobId, parsed.estimatedTime);
|
|
479
|
+
}
|
|
480
|
+
} catch {
|
|
481
|
+
// Not JSON or not an async response, return as-is
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
424
485
|
return result;
|
|
425
486
|
|
|
426
487
|
} catch (error) {
|
|
427
488
|
if (error.code === 'ECONNABORTED') {
|
|
428
|
-
|
|
489
|
+
return {
|
|
490
|
+
content: [
|
|
491
|
+
{
|
|
492
|
+
type: 'text',
|
|
493
|
+
text: `⚠️ Translation Timeout\n\n` +
|
|
494
|
+
`The file is large and taking longer than expected to translate.\n` +
|
|
495
|
+
`This is normal for files over 50KB or with 100+ strings.\n\n` +
|
|
496
|
+
`What's happening:\n` +
|
|
497
|
+
`• The translation is still processing on the server\n` +
|
|
498
|
+
`• Large files are chunked and processed with full 8-step pipeline\n` +
|
|
499
|
+
`• Each chunk ensures terminology consistency\n\n` +
|
|
500
|
+
`Recommendations:\n` +
|
|
501
|
+
`1. Try splitting the file into smaller parts\n` +
|
|
502
|
+
`2. Use the translate_text tool for smaller batches\n` +
|
|
503
|
+
`3. Contact support if this persists\n\n` +
|
|
504
|
+
`File size: ${content.length} characters`
|
|
505
|
+
}
|
|
506
|
+
]
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Check if it's actually a service unavailable error
|
|
511
|
+
if (error.code === 'ECONNREFUSED' ||
|
|
512
|
+
error.code === 'ETIMEDOUT' ||
|
|
513
|
+
error.response?.status === 503 ||
|
|
514
|
+
error.response?.status === 502 ||
|
|
515
|
+
error.response?.status === 504) {
|
|
516
|
+
throw new Error(`Translation service unavailable: ${error.message}`);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// For other errors, throw them as-is without "unavailable" keyword
|
|
520
|
+
throw error;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Format translation result for consistent output
|
|
525
|
+
function formatTranslationResult(parsedResult, texts, targetLanguage, sourceLanguage, targetAudience, industry, region) {
|
|
526
|
+
return {
|
|
527
|
+
translatedTexts: parsedResult?.translatedTexts || [],
|
|
528
|
+
content: [
|
|
529
|
+
{
|
|
530
|
+
type: 'text',
|
|
531
|
+
text: `Translation Results:\n\n` +
|
|
532
|
+
`🌍 ${parsedResult?.sourceLanguage || sourceLanguage || 'Auto-detected'} → ${parsedResult?.targetLanguage || targetLanguage}\n` +
|
|
533
|
+
`👥 Audience: ${parsedResult?.targetAudience || targetAudience}\n` +
|
|
534
|
+
`🏭 Industry: ${parsedResult?.industry || industry}\n` +
|
|
535
|
+
`${parsedResult?.region || region ? `📍 Region: ${parsedResult?.region || region}\n` : ''}` +
|
|
536
|
+
`⏱️ Processing Time: ${parsedResult?.processingTimeMs || 'N/A'}ms\n` +
|
|
537
|
+
`✅ Valid: ${parsedResult?.isValid !== undefined ? parsedResult.isValid : 'N/A'}\n\n` +
|
|
538
|
+
`📝 Translations:\n` +
|
|
539
|
+
(parsedResult?.translatedTexts || []).map((text, index) =>
|
|
540
|
+
`${index + 1}. "${(parsedResult?.originalTexts || texts)[index]}" → "${text}"`
|
|
541
|
+
).join('\n'),
|
|
542
|
+
},
|
|
543
|
+
],
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// Poll for async translation job status
|
|
548
|
+
async function pollTranslationJob(jobId, estimatedTime) {
|
|
549
|
+
const maxPolls = 60; // Max 10 minutes of polling
|
|
550
|
+
const pollInterval = 10000; // Poll every 10 seconds
|
|
551
|
+
|
|
552
|
+
for (let i = 0; i < maxPolls; i++) {
|
|
553
|
+
await new Promise(resolve => setTimeout(resolve, pollInterval));
|
|
554
|
+
|
|
555
|
+
try {
|
|
556
|
+
const statusRequest = {
|
|
557
|
+
jsonrpc: '2.0',
|
|
558
|
+
id: Date.now(),
|
|
559
|
+
method: 'tools/call',
|
|
560
|
+
params: {
|
|
561
|
+
name: 'check_translation_status',
|
|
562
|
+
arguments: { jobId }
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
const response = await axios.post(MCP_SERVER_URL, statusRequest, {
|
|
567
|
+
headers: { 'Content-Type': 'application/json' },
|
|
568
|
+
timeout: 10000
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
if (response.data.error) {
|
|
572
|
+
throw new Error(`Status check error: ${response.data.error.message || response.data.error}`);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const result = response.data.result;
|
|
576
|
+
if (result && result.content && result.content[0]) {
|
|
577
|
+
const status = JSON.parse(result.content[0].text);
|
|
578
|
+
|
|
579
|
+
if (status.status === 'completed') {
|
|
580
|
+
return status.result;
|
|
581
|
+
} else if (status.status === 'failed') {
|
|
582
|
+
throw new Error(`Translation failed: ${status.error}`);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Still processing - continue polling
|
|
586
|
+
console.error(`Translation progress: ${status.progress}% (${status.message})`);
|
|
587
|
+
}
|
|
588
|
+
} catch (error) {
|
|
589
|
+
console.error(`Error polling job status: ${error.message}`);
|
|
590
|
+
// Continue polling even if status check fails
|
|
429
591
|
}
|
|
430
|
-
throw new Error(`Translation service unavailable: ${error.message}`);
|
|
431
592
|
}
|
|
593
|
+
|
|
594
|
+
throw new Error(`Translation job ${jobId} timed out after ${maxPolls * pollInterval / 1000} seconds`);
|
|
432
595
|
}
|
|
433
596
|
|
|
434
597
|
async function handleGetCredits(args) {
|
|
435
598
|
try {
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
arguments: {
|
|
439
|
-
apiKey: API_KEY,
|
|
440
|
-
}
|
|
441
|
-
}, {
|
|
599
|
+
// Get team info first using the API key
|
|
600
|
+
const teamResponse = await axios.get(`https://platform.i18nagent.ai/api/teams/by-api-key/${API_KEY}`, {
|
|
442
601
|
headers: {
|
|
443
602
|
'Content-Type': 'application/json'
|
|
444
603
|
},
|
|
445
604
|
timeout: 10000
|
|
446
605
|
});
|
|
447
606
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
if (result.isError) {
|
|
451
|
-
throw new Error(result.content[0].text);
|
|
607
|
+
if (!teamResponse.data.success) {
|
|
608
|
+
throw new Error(teamResponse.data.error || 'Failed to get team information');
|
|
452
609
|
}
|
|
453
610
|
|
|
454
|
-
const
|
|
611
|
+
const teamInfo = teamResponse.data.data;
|
|
612
|
+
const approximateWordsAvailable = Math.floor(teamInfo.credits * 1000); // 0.001 credits per word
|
|
455
613
|
|
|
456
614
|
return {
|
|
457
615
|
content: [
|
|
@@ -459,11 +617,11 @@ async function handleGetCredits(args) {
|
|
|
459
617
|
type: 'text',
|
|
460
618
|
text: `💰 **Credits Information**
|
|
461
619
|
|
|
462
|
-
🏢 **Team**: ${
|
|
463
|
-
💳 **Credits Remaining**: ${
|
|
464
|
-
📝 **Approximate Words Available**: ${
|
|
465
|
-
💵 **Cost per Word**:
|
|
466
|
-
⏰ **Last Updated**: ${new Date(
|
|
620
|
+
🏢 **Team**: ${teamInfo.name}
|
|
621
|
+
💳 **Credits Remaining**: ${teamInfo.credits}
|
|
622
|
+
📝 **Approximate Words Available**: ${approximateWordsAvailable.toLocaleString()}
|
|
623
|
+
💵 **Cost per Word**: 0.001 credits
|
|
624
|
+
⏰ **Last Updated**: ${new Date().toLocaleString()}
|
|
467
625
|
|
|
468
626
|
Note: Word count is approximate and may vary based on actual content complexity and translation requirements.`,
|
|
469
627
|
},
|
|
@@ -808,7 +966,7 @@ async function main() {
|
|
|
808
966
|
await server.connect(transport);
|
|
809
967
|
console.error('i18n-agent MCP server running...');
|
|
810
968
|
console.error('MCP_SERVER_URL:', MCP_SERVER_URL);
|
|
811
|
-
console.error('API_KEY:', API_KEY
|
|
969
|
+
console.error('API_KEY:', API_KEY);
|
|
812
970
|
}
|
|
813
971
|
|
|
814
972
|
main().catch((error) => {
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@i18n-agent/mcp-client",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "MCP client for i18n-agent translation service - supports Claude, Cursor, VS Code, and other AI IDEs",
|
|
5
5
|
"main": "mcp-client.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"i18n-agent-install": "
|
|
7
|
+
"i18n-agent-install": "install.js"
|
|
8
8
|
},
|
|
9
9
|
"type": "module",
|
|
10
10
|
"scripts": {
|
|
@@ -50,4 +50,4 @@
|
|
|
50
50
|
"publishConfig": {
|
|
51
51
|
"access": "public"
|
|
52
52
|
}
|
|
53
|
-
}
|
|
53
|
+
}
|