ppt-translator-mcp 0.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.
package/.env.example ADDED
@@ -0,0 +1,4 @@
1
+ AWS_ACCESS_KEY_ID=your_access_key
2
+ AWS_SECRET_ACCESS_KEY=your_secret_key
3
+ AWS_REGION=us-east-1
4
+ DEFAULT_TARGET_LANGUAGE=zh-CN
package/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # PowerPoint Translator MCP Service
2
+
3
+ A Model Context Protocol (MCP) service that provides PowerPoint translation capabilities using AWS Bedrock models.
4
+
5
+ ## Features
6
+
7
+ - Translate PowerPoint presentations to multiple languages
8
+ - Preserve formatting during translation
9
+ - Support for multiple translation engines (Nova Lite and Claude)
10
+ - Intelligent handling of proper nouns, brand names, and special content
11
+
12
+ ## Supported Languages
13
+
14
+ - Simplified Chinese (zh-CN)
15
+ - Traditional Chinese (zh-TW)
16
+ - English (en)
17
+ - Japanese (ja)
18
+ - Korean (ko)
19
+ - French (fr)
20
+ - German (de)
21
+ - Spanish (es)
22
+
23
+ ## Prerequisites
24
+
25
+ - Node.js 14+ (for npm package installation)
26
+ - Python 3.8+
27
+ - AWS account with Bedrock access
28
+ - AWS credentials configured
29
+
30
+ ## Installation
31
+
32
+ ### Using npm
33
+
34
+ ```bash
35
+ npm install -g ppt-translator-mcp-zhu2mu-unique
36
+ ```
37
+
38
+ ### Using Amazon Q Configuration
39
+
40
+ Add the following to your Amazon Q configuration:
41
+
42
+ ```json
43
+ "mcpServers": {
44
+ "ppt-translator": {
45
+ "timeout": 60,
46
+ "type": "stdio",
47
+ "command": "npx",
48
+ "args": [
49
+ "-y",
50
+ "ppt-translator-mcp-zhu2mu-unique@latest"
51
+ ],
52
+ "env": {
53
+ "AWS_ACCESS_KEY_ID": "${AWS_ACCESS_KEY_ID}",
54
+ "AWS_SECRET_ACCESS_KEY": "${AWS_SECRET_ACCESS_KEY}",
55
+ "AWS_REGION": "us-east-1",
56
+ "DEFAULT_TARGET_LANGUAGE": "zh-CN"
57
+ }
58
+ }
59
+ }
60
+ ```
61
+
62
+ ## Usage
63
+
64
+ Once the MCP server is running, you can use it with any MCP-compatible client like Amazon Q or Claude Desktop.
65
+
66
+ ### Available Tools
67
+
68
+ 1. `translate_ppt` - Translate a PowerPoint document
69
+ - Parameters:
70
+ - `input_file`: Path to the input PowerPoint file (required)
71
+ - `target_language`: Target language code (default: zh-CN)
72
+ - `output_file`: Path to save the translated file (optional)
73
+ - `translation_method`: Translation method, 'nova' or 'claude' (default: nova)
74
+
75
+ 2. `list_supported_languages` - List all supported target languages
76
+
77
+ ## Example
78
+
79
+ ```
80
+ Translate my presentation.pptx to Japanese using the Claude model
81
+ ```
82
+
83
+ ## Development
84
+
85
+ ### Local Development
86
+
87
+ 1. Clone this repository:
88
+ ```bash
89
+ git clone https://github.com/yourusername/mcpppttranslator.git
90
+ cd mcpppttranslator
91
+ ```
92
+
93
+ 2. Install dependencies:
94
+ ```bash
95
+ npm install
96
+ pip install -r requirements.txt
97
+ ```
98
+
99
+ 3. Run the server:
100
+ ```bash
101
+ node index.js
102
+ ```
103
+
104
+ ### Publishing to npm
105
+
106
+ 1. Update version in package.json
107
+ 2. Login to npm:
108
+ ```bash
109
+ npm login
110
+ ```
111
+ 3. Publish:
112
+ ```bash
113
+ npm publish --access public
114
+ ```
115
+
116
+ ## License
117
+
118
+ MIT
@@ -0,0 +1,19 @@
1
+ {
2
+ "mcpServers": {
3
+ "ppt-translator": {
4
+ "timeout": 60,
5
+ "type": "stdio",
6
+ "command": "npx",
7
+ "args": [
8
+ "-y",
9
+ "ppt-translator-mcp@latest"
10
+ ],
11
+ "env": {
12
+ "AWS_ACCESS_KEY_ID": "${AWS_ACCESS_KEY_ID}",
13
+ "AWS_SECRET_ACCESS_KEY": "${AWS_SECRET_ACCESS_KEY}",
14
+ "AWS_REGION": "us-east-1",
15
+ "DEFAULT_TARGET_LANGUAGE": "zh-CN"
16
+ }
17
+ }
18
+ }
19
+ }
package/index.js ADDED
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require('child_process');
4
+ const path = require('path');
5
+ const fs = require('fs');
6
+
7
+ // Ensure Python and pip are installed
8
+ function checkPythonDependencies() {
9
+ try {
10
+ // Check if Python is available
11
+ const pythonVersion = spawn('python3', ['--version']);
12
+ pythonVersion.on('error', (err) => {
13
+ console.error('Python3 is not installed or not in PATH. Please install Python 3.8+');
14
+ process.exit(1);
15
+ });
16
+
17
+ // Check if pip is available
18
+ const pipVersion = spawn('pip3', ['--version']);
19
+ pipVersion.on('error', (err) => {
20
+ console.error('pip3 is not installed or not in PATH. Please install pip');
21
+ process.exit(1);
22
+ });
23
+ } catch (error) {
24
+ console.error('Error checking Python dependencies:', error);
25
+ process.exit(1);
26
+ }
27
+ }
28
+
29
+ // Install required Python packages
30
+ function installPythonDependencies() {
31
+ return new Promise((resolve, reject) => {
32
+ console.log('Installing Python dependencies...');
33
+
34
+ // Path to requirements.txt in the package
35
+ const requirementsPath = path.join(__dirname, 'requirements.txt');
36
+
37
+ // Install dependencies using pip
38
+ const pip = spawn('pip3', ['install', '-r', requirementsPath]);
39
+
40
+ pip.stdout.on('data', (data) => {
41
+ console.log(`${data}`);
42
+ });
43
+
44
+ pip.stderr.on('data', (data) => {
45
+ console.error(`${data}`);
46
+ });
47
+
48
+ pip.on('close', (code) => {
49
+ if (code === 0) {
50
+ console.log('Python dependencies installed successfully');
51
+ resolve();
52
+ } else {
53
+ console.error(`pip exited with code ${code}`);
54
+ reject(new Error(`Failed to install Python dependencies (exit code: ${code})`));
55
+ }
56
+ });
57
+ });
58
+ }
59
+
60
+ // Run the MCP server
61
+ function runMCPServer() {
62
+ console.log('Starting PowerPoint Translator MCP server...');
63
+
64
+ // Path to the Python server script
65
+ const serverPath = path.join(__dirname, 'server.py');
66
+
67
+ // Make sure the script is executable
68
+ fs.chmodSync(serverPath, '755');
69
+
70
+ // Run the Python script in MCP mode
71
+ const server = spawn('python3', [serverPath]);
72
+
73
+ // Pipe stdin/stdout directly to the Python process for MCP communication
74
+ process.stdin.pipe(server.stdin);
75
+ server.stdout.pipe(process.stdout);
76
+ server.stderr.on('data', (data) => {
77
+ process.stderr.write(`${data}`);
78
+ });
79
+
80
+ server.on('close', (code) => {
81
+ console.log(`MCP server exited with code ${code}`);
82
+ process.exit(code);
83
+ });
84
+
85
+ // Handle process termination
86
+ process.on('SIGINT', () => {
87
+ console.log('Received SIGINT. Shutting down MCP server...');
88
+ server.kill('SIGINT');
89
+ });
90
+
91
+ process.on('SIGTERM', () => {
92
+ console.log('Received SIGTERM. Shutting down MCP server...');
93
+ server.kill('SIGTERM');
94
+ });
95
+ }
96
+
97
+ // Main function
98
+ async function main() {
99
+ try {
100
+ checkPythonDependencies();
101
+ await installPythonDependencies();
102
+ runMCPServer();
103
+ } catch (error) {
104
+ console.error('Error starting MCP server:', error);
105
+ process.exit(1);
106
+ }
107
+ }
108
+
109
+ // Run the main function
110
+ main();
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "ppt-translator-mcp",
3
+ "version": "0.0.1",
4
+ "description": "MCP server for PowerPoint translation using AWS Bedrock",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "mcp-ppt-translator": "./index.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node index.js"
11
+ },
12
+ "keywords": [
13
+ "mcp",
14
+ "powerpoint",
15
+ "translation",
16
+ "aws",
17
+ "bedrock"
18
+ ],
19
+ "author": "zhuermu",
20
+ "license": "MIT",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/zhuermu/mcpppttranslator.git"
24
+ },
25
+ "bugs": {
26
+ "url": "https://github.com/zhuermu/mcpppttranslator/issues"
27
+ },
28
+ "homepage": "https://github.com/zhuermu/mcpppttranslator#readme",
29
+ "dependencies": {
30
+ "@modelcontextprotocol/server": "^1.0.0"
31
+ }
32
+ }
package/publish.sh ADDED
@@ -0,0 +1,22 @@
1
+ #!/bin/bash
2
+
3
+ # 检查是否提供了NPM令牌
4
+ if [ -z "$1" ]; then
5
+ echo "请提供NPM令牌作为参数"
6
+ echo "用法: ./publish.sh your-npm-token"
7
+ exit 1
8
+ fi
9
+
10
+ # 设置NPM令牌
11
+ NPM_TOKEN=$1
12
+
13
+ # 创建临时.npmrc文件
14
+ echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc
15
+
16
+ # 发布包
17
+ npm publish --access public
18
+
19
+ # 删除临时.npmrc文件
20
+ rm .npmrc
21
+
22
+ echo "发布完成!"
@@ -0,0 +1,3 @@
1
+ python-pptx>=0.6.21
2
+ boto3>=1.28.0
3
+ python-dotenv>=1.0.0
package/server.py ADDED
@@ -0,0 +1,565 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ PPT Translator Server
4
+ A server that provides PowerPoint translation capabilities using AWS Bedrock models.
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ import logging
10
+ import json
11
+ import argparse
12
+ from typing import Dict, Any, Optional, List
13
+ from pathlib import Path
14
+
15
+ try:
16
+ from pptx import Presentation
17
+ except ImportError:
18
+ print("Error: python-pptx not found. Please install it with 'pip install python-pptx'")
19
+ sys.exit(1)
20
+
21
+ try:
22
+ import boto3
23
+ except ImportError:
24
+ print("Error: boto3 not found. Please install it with 'pip install boto3'")
25
+ sys.exit(1)
26
+
27
+ try:
28
+ from dotenv import load_dotenv
29
+ except ImportError:
30
+ print("Error: python-dotenv not found. Please install it with 'pip install python-dotenv'")
31
+ sys.exit(1)
32
+
33
+ # Load environment variables
34
+ load_dotenv()
35
+
36
+ # Configure logging
37
+ logging.basicConfig(level=logging.INFO)
38
+ logger = logging.getLogger(__name__)
39
+
40
+ # Initialize AWS Bedrock client
41
+ try:
42
+ bedrock_client = boto3.client(
43
+ 'bedrock-runtime',
44
+ aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'),
45
+ aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY'),
46
+ region_name=os.getenv('AWS_REGION', 'us-east-1')
47
+ )
48
+ except Exception as e:
49
+ logger.error(f"Failed to initialize AWS Bedrock client: {str(e)}")
50
+ print(f"Error: Failed to initialize AWS Bedrock client. Please check your AWS credentials.")
51
+ sys.exit(1)
52
+
53
+ default_target_language = os.getenv('DEFAULT_TARGET_LANGUAGE', 'zh-CN')
54
+
55
+ def _translate_text(text: str, target_language: str, method: str) -> Optional[str]:
56
+ """Translate a single text"""
57
+ try:
58
+ if method == 'claude':
59
+ return _translate_with_claude(text, target_language)
60
+ else:
61
+ return _translate_with_nova(text, target_language)
62
+ except Exception as e:
63
+ logger.error(f"Failed to translate text: {str(e)}")
64
+ return None
65
+
66
+ def _translate_with_claude(text: str, target_language: str) -> str:
67
+ """Translate using AWS Bedrock Claude 3.5 Sonnet"""
68
+ language_map = {
69
+ 'zh-CN': '简体中文',
70
+ 'zh-TW': '繁体中文',
71
+ 'en': '英语',
72
+ 'ja': '日语',
73
+ 'ko': '韩语',
74
+ 'fr': '法语',
75
+ 'de': '德语',
76
+ 'es': '西班牙语'
77
+ }
78
+
79
+ target_lang_name = language_map.get(target_language, target_language)
80
+
81
+ body = json.dumps({
82
+ "anthropic_version": "bedrock-2023-05-31",
83
+ "max_tokens": 1000,
84
+ "messages": [
85
+ {
86
+ "role": "user",
87
+ "content": f"Please translate the following text to {target_lang_name}. Keep proper nouns, brand names, person names, place names, company names, and currencies untranslated. Only return the translation result:\n\n{text}"
88
+ }
89
+ ]
90
+ })
91
+
92
+ response = bedrock_client.invoke_model(
93
+ body=body,
94
+ modelId="anthropic.claude-3-5-sonnet-20241022-v2:0",
95
+ accept="application/json",
96
+ contentType="application/json"
97
+ )
98
+
99
+ response_body = json.loads(response.get('body').read())
100
+ return response_body['content'][0]['text'].strip()
101
+
102
+ def _translate_with_nova(text: str, target_language: str) -> str:
103
+ """Translate using AWS Bedrock Nova Lite"""
104
+ language_map = {
105
+ 'zh-CN': '简体中文',
106
+ 'zh-TW': '繁体中文',
107
+ 'en': '英语',
108
+ 'ja': '日语',
109
+ 'ko': '韩语',
110
+ 'fr': '法语',
111
+ 'de': '德语',
112
+ 'es': '西班牙语'
113
+ }
114
+
115
+ target_lang_name = language_map.get(target_language, target_language)
116
+
117
+ # Check if it's a proper noun or number
118
+ if text.strip().isdigit() or (len(text.strip()) <= 3 and not any(c.isalpha() for c in text)):
119
+ return text
120
+
121
+ body = json.dumps({
122
+ "messages": [
123
+ {
124
+ "role": "user",
125
+ "content": [
126
+ {
127
+ "text": f"""Translate the following text to {target_lang_name}.
128
+
129
+ Please follow these rules:
130
+ 1. Keep all brand names untranslated, such as Amazon, AWS, Bedrock, Nova, etc.
131
+ 2. Keep all person names untranslated
132
+ 3. Keep all company names untranslated
133
+ 4. Keep all product names untranslated
134
+ 5. Keep all currency amounts untranslated
135
+ 6. For content that shouldn't be translated, return the original text
136
+ 7. Only return the translation result, without any explanations or original text
137
+
138
+ Original text: {text}"""
139
+ }
140
+ ]
141
+ }
142
+ ],
143
+ "inferenceConfig": {
144
+ "max_new_tokens": 1000,
145
+ "temperature": 0.1
146
+ }
147
+ })
148
+
149
+ response = bedrock_client.invoke_model(
150
+ body=body,
151
+ modelId="amazon.nova-lite-v1:0",
152
+ accept="application/json",
153
+ contentType="application/json"
154
+ )
155
+
156
+ response_body = json.loads(response.get('body').read())
157
+ translated_text = response_body['output']['message']['content'][0]['text'].strip()
158
+
159
+ # If the translation result contains "important rules" or "don't translate", return the original text
160
+ if "重要规则" in translated_text or "不要翻译" in translated_text:
161
+ return text
162
+
163
+ return translated_text
164
+
165
+ def _translate_ppt(input_file: str, output_file: str, target_language: str, method: str) -> Dict[str, Any]:
166
+ """Translate a PowerPoint file"""
167
+ try:
168
+ # Load the PPT
169
+ prs = Presentation(input_file)
170
+ translated_count = 0
171
+
172
+ # Iterate through all slides
173
+ for slide_idx, slide in enumerate(prs.slides):
174
+ logger.info(f"Processing slide {slide_idx + 1}")
175
+
176
+ # Iterate through all shapes in the slide
177
+ for shape in slide.shapes:
178
+ try:
179
+ if hasattr(shape, "text") and shape.text.strip():
180
+ original_text = shape.text.strip()
181
+
182
+ # Translate the text
183
+ translated_text = _translate_text(original_text, target_language, method)
184
+
185
+ if translated_text and translated_text != original_text:
186
+ # Update the text while preserving formatting
187
+ if hasattr(shape, 'text_frame') and shape.text_frame:
188
+ _update_text_frame_with_formatting(shape.text_frame, translated_text)
189
+ else:
190
+ shape.text = translated_text
191
+
192
+ translated_count += 1
193
+ logger.info(f"Translated: '{original_text[:50]}...' -> '{translated_text[:50]}...'")
194
+ except Exception as e:
195
+ logger.error(f"Error processing shape: {str(e)}")
196
+ continue
197
+
198
+ # Save the translated PPT
199
+ prs.save(output_file)
200
+ logger.info(f"Translation completed, saved to: {output_file}")
201
+
202
+ return {"translated_count": translated_count}
203
+ except Exception as e:
204
+ logger.error(f"Error translating PPT: {str(e)}")
205
+ raise
206
+
207
+ def _update_text_frame_with_formatting(text_frame, new_text):
208
+ """Update text frame content while preserving original formatting"""
209
+ try:
210
+ if not text_frame.paragraphs:
211
+ return
212
+
213
+ # Save the format of the first paragraph
214
+ first_paragraph = text_frame.paragraphs[0]
215
+ if first_paragraph.runs:
216
+ # Save the format of the first run
217
+ first_run = first_paragraph.runs[0]
218
+ font_name = first_run.font.name
219
+ font_size = first_run.font.size
220
+ font_bold = first_run.font.bold
221
+ font_italic = first_run.font.italic
222
+
223
+ # Default to black
224
+ from pptx.dml.color import RGBColor
225
+ font_color = RGBColor(0, 0, 0) # Default black
226
+
227
+ # Safely get the color
228
+ try:
229
+ if hasattr(first_run.font, 'color'):
230
+ if hasattr(first_run.font.color, 'rgb') and first_run.font.color.rgb:
231
+ font_color = first_run.font.color.rgb
232
+ elif hasattr(first_run.font.color, 'type'):
233
+ # If it's a theme color, we still use default black
234
+ pass
235
+ except Exception as e:
236
+ logger.error(f"Error getting font color: {str(e)}")
237
+
238
+ # Clear all paragraphs
239
+ text_frame.clear()
240
+
241
+ # Add new text and apply original formatting
242
+ paragraph = text_frame.paragraphs[0]
243
+ # The correct method is to call add_run() directly on the paragraph, not on the runs collection
244
+ run = paragraph.add_run()
245
+ run.text = new_text
246
+
247
+ # Restore formatting
248
+ if font_name:
249
+ run.font.name = font_name
250
+ if font_size:
251
+ run.font.size = font_size
252
+ if font_bold is not None:
253
+ run.font.bold = font_bold
254
+ if font_italic is not None:
255
+ run.font.italic = font_italic
256
+
257
+ # Set font color
258
+ run.font.color.rgb = font_color
259
+ else:
260
+ # If there are no runs, set the text directly
261
+ text_frame.text = new_text
262
+ except Exception as e:
263
+ logger.error(f"Error updating text formatting: {str(e)}")
264
+ # If formatting fails, set the text directly
265
+ text_frame.text = new_text
266
+
267
+ def translate_ppt(
268
+ input_file: str,
269
+ target_language: str = default_target_language,
270
+ output_file: str = None,
271
+ translation_method: str = "nova"
272
+ ) -> Dict[str, Any]:
273
+ """Translate a PowerPoint document to the specified language
274
+
275
+ Args:
276
+ input_file: Path to the input PowerPoint file
277
+ target_language: Target language code, default is zh-CN
278
+ output_file: Path to save the translated file, if not provided it will be auto-generated
279
+ translation_method: Translation method, can be 'nova' or 'claude'
280
+
281
+ Returns:
282
+ Dict[str, Any]: Translation result information
283
+ """
284
+ try:
285
+ if not input_file:
286
+ return {"error": "No input file path provided"}
287
+
288
+ if not Path(input_file).exists():
289
+ return {"error": f"File does not exist: {input_file}"}
290
+
291
+ # Generate output filename
292
+ if not output_file:
293
+ input_path = Path(input_file)
294
+ output_file = str(input_path.parent / f"{input_path.stem}_translated_{target_language}{input_path.suffix}")
295
+
296
+ # Execute translation
297
+ result = _translate_ppt(input_file, output_file, target_language, translation_method)
298
+
299
+ return {
300
+ "success": True,
301
+ "input_file": input_file,
302
+ "output_file": output_file,
303
+ "target_language": target_language,
304
+ "translation_method": translation_method,
305
+ "translated_texts_count": result.get('translated_count', 0),
306
+ "message": "PowerPoint translation completed"
307
+ }
308
+
309
+ except Exception as e:
310
+ logger.error(f"Error processing request: {str(e)}")
311
+ return {"error": f"Processing failed: {str(e)}"}
312
+
313
+ def list_supported_languages() -> Dict[str, str]:
314
+ """List all supported target languages for translation
315
+
316
+ Returns:
317
+ Dict[str, str]: Dictionary of language codes and their names
318
+ """
319
+ return {
320
+ "zh-CN": "Simplified Chinese",
321
+ "zh-TW": "Traditional Chinese",
322
+ "en": "English",
323
+ "ja": "Japanese",
324
+ "ko": "Korean",
325
+ "fr": "French",
326
+ "de": "German",
327
+ "es": "Spanish"
328
+ }
329
+
330
+ # MCP protocol implementation
331
+ def handle_mcp_request():
332
+ """Handle MCP protocol requests"""
333
+ try:
334
+ # Read request from stdin
335
+ request_line = sys.stdin.readline().strip()
336
+ request = json.loads(request_line)
337
+
338
+ # Process the request
339
+ if request.get("type") == "invoke":
340
+ tool_name = request.get("name")
341
+ params = request.get("params", {})
342
+
343
+ if tool_name == "translate_ppt":
344
+ result = translate_ppt(
345
+ input_file=params.get("input_file"),
346
+ target_language=params.get("target_language", default_target_language),
347
+ output_file=params.get("output_file"),
348
+ translation_method=params.get("translation_method", "nova")
349
+ )
350
+
351
+ # Send response
352
+ response = {
353
+ "type": "result",
354
+ "result": result
355
+ }
356
+ sys.stdout.write(json.dumps(response) + "\n")
357
+ sys.stdout.flush()
358
+
359
+ elif tool_name == "list_supported_languages":
360
+ result = list_supported_languages()
361
+
362
+ # Send response
363
+ response = {
364
+ "type": "result",
365
+ "result": result
366
+ }
367
+ sys.stdout.write(json.dumps(response) + "\n")
368
+ sys.stdout.flush()
369
+
370
+ else:
371
+ # Unknown tool
372
+ response = {
373
+ "type": "error",
374
+ "error": f"Unknown tool: {tool_name}"
375
+ }
376
+ sys.stdout.write(json.dumps(response) + "\n")
377
+ sys.stdout.flush()
378
+
379
+ elif request.get("type") == "describe":
380
+ # Send server description
381
+ response = {
382
+ "type": "description",
383
+ "name": "PowerPoint Translator",
384
+ "description": "A service that provides PowerPoint translation capabilities using AWS Bedrock models",
385
+ "tools": [
386
+ {
387
+ "name": "translate_ppt",
388
+ "description": "Translate PowerPoint documents to a specified language",
389
+ "parameters": {
390
+ "type": "object",
391
+ "properties": {
392
+ "input_file": {
393
+ "type": "string",
394
+ "description": "Path to the input PowerPoint file"
395
+ },
396
+ "target_language": {
397
+ "type": "string",
398
+ "description": "Target language code (e.g., zh-CN, en, ja, ko, fr, de, es)",
399
+ "default": default_target_language
400
+ },
401
+ "output_file": {
402
+ "type": "string",
403
+ "description": "Path to save the translated PowerPoint file (if not provided, will be auto-generated)"
404
+ },
405
+ "translation_method": {
406
+ "type": "string",
407
+ "description": "Translation method to use: 'nova' (faster) or 'claude' (higher quality)",
408
+ "default": "nova",
409
+ "enum": ["nova", "claude"]
410
+ }
411
+ },
412
+ "required": ["input_file"]
413
+ }
414
+ },
415
+ {
416
+ "name": "list_supported_languages",
417
+ "description": "List all supported target languages for translation",
418
+ "parameters": {
419
+ "type": "object",
420
+ "properties": {}
421
+ }
422
+ }
423
+ ]
424
+ }
425
+ sys.stdout.write(json.dumps(response) + "\n")
426
+ sys.stdout.flush()
427
+
428
+ else:
429
+ # Unknown request type
430
+ response = {
431
+ "type": "error",
432
+ "error": f"Unknown request type: {request.get('type')}"
433
+ }
434
+ sys.stdout.write(json.dumps(response) + "\n")
435
+ sys.stdout.flush()
436
+
437
+ except Exception as e:
438
+ logger.error(f"Error handling MCP request: {str(e)}")
439
+ response = {
440
+ "type": "error",
441
+ "error": f"Error handling request: {str(e)}"
442
+ }
443
+ sys.stdout.write(json.dumps(response) + "\n")
444
+ sys.stdout.flush()
445
+
446
+ def main():
447
+ """Main function"""
448
+ parser = argparse.ArgumentParser(description='PowerPoint Translator')
449
+ parser.add_argument('--mcp', action='store_true', help='Run in MCP mode')
450
+ parser.add_argument('--translate', action='store_true', help='Translate a PowerPoint file')
451
+ parser.add_argument('--input-file', help='Path to the input PowerPoint file')
452
+ parser.add_argument('--target-language', default=default_target_language, help='Target language code')
453
+ parser.add_argument('--output-file', help='Path to save the translated file')
454
+ parser.add_argument('--method', default='nova', choices=['nova', 'claude'], help='Translation method')
455
+ parser.add_argument('--list-languages', action='store_true', help='List supported languages')
456
+
457
+ args = parser.parse_args()
458
+
459
+ if args.mcp:
460
+ print("Starting PowerPoint Translator in MCP mode...")
461
+ while True:
462
+ try:
463
+ handle_mcp_request()
464
+ except KeyboardInterrupt:
465
+ break
466
+ except Exception as e:
467
+ logger.error(f"Error in MCP mode: {str(e)}")
468
+ break
469
+
470
+ elif args.translate:
471
+ if not args.input_file:
472
+ print("Error: Input file is required for translation")
473
+ parser.print_help()
474
+ sys.exit(1)
475
+
476
+ result = translate_ppt(
477
+ args.input_file,
478
+ args.target_language,
479
+ args.output_file,
480
+ args.method
481
+ )
482
+
483
+ if 'error' in result:
484
+ print(f"Error: {result['error']}")
485
+ sys.exit(1)
486
+ else:
487
+ print(f"Translation completed successfully!")
488
+ print(f"Input file: {result['input_file']}")
489
+ print(f"Output file: {result['output_file']}")
490
+ print(f"Target language: {result['target_language']}")
491
+ print(f"Translation method: {result['translation_method']}")
492
+ print(f"Translated {result['translated_texts_count']} text elements")
493
+
494
+ elif args.list_languages:
495
+ languages = list_supported_languages()
496
+ print("Supported languages:")
497
+ for code, name in languages.items():
498
+ print(f" {code}: {name}")
499
+
500
+ else:
501
+ # Default to MCP mode when no arguments are provided
502
+ print("Starting PowerPoint Translator in MCP mode...")
503
+ # Send initial description
504
+ response = {
505
+ "type": "description",
506
+ "name": "PowerPoint Translator",
507
+ "description": "A service that provides PowerPoint translation capabilities using AWS Bedrock models",
508
+ "tools": [
509
+ {
510
+ "name": "translate_ppt",
511
+ "description": "Translate PowerPoint documents to a specified language",
512
+ "parameters": {
513
+ "type": "object",
514
+ "properties": {
515
+ "input_file": {
516
+ "type": "string",
517
+ "description": "Path to the input PowerPoint file"
518
+ },
519
+ "target_language": {
520
+ "type": "string",
521
+ "description": "Target language code (e.g., zh-CN, en, ja, ko, fr, de, es)",
522
+ "default": default_target_language
523
+ },
524
+ "output_file": {
525
+ "type": "string",
526
+ "description": "Path to save the translated PowerPoint file (if not provided, will be auto-generated)"
527
+ },
528
+ "translation_method": {
529
+ "type": "string",
530
+ "description": "Translation method to use: 'nova' (faster) or 'claude' (higher quality)",
531
+ "default": "nova",
532
+ "enum": ["nova", "claude"]
533
+ }
534
+ },
535
+ "required": ["input_file"]
536
+ }
537
+ },
538
+ {
539
+ "name": "list_supported_languages",
540
+ "description": "List all supported target languages for translation",
541
+ "parameters": {
542
+ "type": "object",
543
+ "properties": {}
544
+ }
545
+ }
546
+ ]
547
+ }
548
+ sys.stdout.write(json.dumps(response) + "\n")
549
+ sys.stdout.flush()
550
+
551
+ while True:
552
+ try:
553
+ handle_mcp_request()
554
+ except KeyboardInterrupt:
555
+ break
556
+ except Exception as e:
557
+ logger.error(f"Error in MCP mode: {str(e)}")
558
+ break
559
+
560
+ if __name__ == "__main__":
561
+ try:
562
+ main()
563
+ except Exception as e:
564
+ logger.error(f"Error: {str(e)}")
565
+ sys.exit(1)
package/test_local.sh ADDED
@@ -0,0 +1,42 @@
1
+ #!/bin/bash
2
+
3
+ # This script tests the PowerPoint Translator MCP service locally
4
+
5
+ # Get the directory of this script
6
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
+
8
+ # Install dependencies
9
+ echo "Installing dependencies..."
10
+ pip install -r "$SCRIPT_DIR/requirements.txt"
11
+
12
+ # Create a test PowerPoint file if it doesn't exist
13
+ if [ ! -f "$SCRIPT_DIR/test.pptx" ]; then
14
+ echo "Creating test PowerPoint file..."
15
+ python -c "
16
+ from pptx import Presentation
17
+ from pptx.util import Inches
18
+
19
+ # Create a presentation
20
+ prs = Presentation()
21
+
22
+ # Add a slide with a title and content
23
+ slide_layout = prs.slide_layouts[1] # Title and content layout
24
+ slide = prs.slides.add_slide(slide_layout)
25
+
26
+ # Set the title
27
+ title = slide.shapes.title
28
+ title.text = 'Test PowerPoint for Translation'
29
+
30
+ # Add content
31
+ content = slide.placeholders[1]
32
+ content.text = 'This is a test PowerPoint file for translation.\n\nIt contains some text that will be translated to another language.\n\nAWS Bedrock is a powerful service for AI/ML tasks.'
33
+
34
+ # Save the presentation
35
+ prs.save('$SCRIPT_DIR/test.pptx')
36
+ print('Test PowerPoint file created at $SCRIPT_DIR/test.pptx')
37
+ "
38
+ fi
39
+
40
+ # Run the MCP server
41
+ echo "Starting PowerPoint Translator MCP server..."
42
+ node "$SCRIPT_DIR/index.js"