Add MCP server and configuration for AI assistant integration
This commit is contained in:
parent
e48ea07e44
commit
a4ecd3e0ec
368
README.md
368
README.md
@ -1,303 +1,267 @@
|
|||||||
# Document Translation API
|
# 📄 Document Translation API
|
||||||
|
|
||||||
A powerful Python API for translating complex structured documents (Excel, Word, PowerPoint) while **strictly preserving** the original formatting, layout, and embedded media.
|
A powerful Python API for translating complex structured documents (Excel, Word, PowerPoint) while **strictly preserving** the original formatting, layout, and embedded media.
|
||||||
|
|
||||||
## 🎯 Features
|
## ✨ Features
|
||||||
|
|
||||||
### Excel Translation (.xlsx)
|
### 🔄 Multiple Translation Providers
|
||||||
|
| Provider | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| **Google Translate** | Cloud | Free, fast, reliable |
|
||||||
|
| **Ollama** | Local LLM | Privacy-focused, customizable with system prompts |
|
||||||
|
| **WebLLM** | Browser | Runs entirely in browser using WebGPU |
|
||||||
|
| **DeepL** | Cloud | High-quality translations (API key required) |
|
||||||
|
| **LibreTranslate** | Self-hosted | Open-source alternative |
|
||||||
|
|
||||||
|
### 📊 Excel Translation (.xlsx)
|
||||||
- ✅ Translates all cell content and sheet names
|
- ✅ Translates all cell content and sheet names
|
||||||
- ✅ Preserves cell merging
|
- ✅ Preserves cell merging, formulas, and styles
|
||||||
- ✅ Maintains font styles (size, bold, italic, color)
|
- ✅ Maintains font styles, colors, and borders
|
||||||
- ✅ Keeps background colors and borders
|
- ✅ Image text extraction with vision models
|
||||||
- ✅ Translates text within formulas while preserving formula structure
|
- ✅ Adds translated image text as comments
|
||||||
- ✅ Retains embedded images in original positions
|
|
||||||
|
|
||||||
### Word Translation (.docx)
|
### 📝 Word Translation (.docx)
|
||||||
- ✅ Translates body text, headers, footers, and tables
|
- ✅ Translates body text, headers, footers, and tables
|
||||||
- ✅ Preserves heading styles and paragraph formatting
|
- ✅ Preserves heading styles and paragraph formatting
|
||||||
- ✅ Maintains lists (numbered/bulleted)
|
- ✅ Maintains lists, images, charts, and SmartArt
|
||||||
- ✅ Keeps embedded images, charts, and SmartArt in place
|
- ✅ Image text extraction and translation
|
||||||
- ✅ Preserves table structures and cell formatting
|
|
||||||
|
|
||||||
### PowerPoint Translation (.pptx)
|
### 📽️ PowerPoint Translation (.pptx)
|
||||||
- ✅ Translates slide titles, body text, and speaker notes
|
- ✅ Translates slide titles, body text, and speaker notes
|
||||||
- ✅ Preserves slide layouts and transitions
|
- ✅ Preserves slide layouts, transitions, and animations
|
||||||
- ✅ Maintains animations
|
- ✅ Image text extraction with text boxes added below images
|
||||||
- ✅ Keeps images, videos, and shapes in exact positions
|
- ✅ Keeps layering order and positions
|
||||||
- ✅ Preserves layering order
|
|
||||||
|
### 🧠 LLM Features (Ollama/WebLLM)
|
||||||
|
- ✅ **Custom System Prompts**: Provide context for better translations
|
||||||
|
- ✅ **Technical Glossary**: Define term mappings (e.g., `batterie=coil`)
|
||||||
|
- ✅ **Presets**: HVAC, IT, Legal, Medical terminology
|
||||||
|
- ✅ **Vision Models**: Translate text within images (gemma3, qwen3-vl, llava)
|
||||||
|
|
||||||
## 🚀 Quick Start
|
## 🚀 Quick Start
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
1. **Clone the repository:**
|
|
||||||
```powershell
|
```powershell
|
||||||
git clone <repository-url>
|
# Clone the repository
|
||||||
cd Translate
|
git clone https://gitea.parsanet.org/sepehr/office_translator.git
|
||||||
```
|
cd office_translator
|
||||||
|
|
||||||
2. **Create a virtual environment:**
|
# Create virtual environment
|
||||||
```powershell
|
|
||||||
python -m venv venv
|
python -m venv venv
|
||||||
.\venv\Scripts\Activate.ps1
|
.\venv\Scripts\Activate.ps1
|
||||||
```
|
|
||||||
|
|
||||||
3. **Install dependencies:**
|
# Install dependencies
|
||||||
```powershell
|
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
```
|
|
||||||
|
|
||||||
4. **Configure environment:**
|
# Run the API
|
||||||
```powershell
|
|
||||||
cp .env.example .env
|
|
||||||
# Edit .env with your preferred settings
|
|
||||||
```
|
|
||||||
|
|
||||||
5. **Run the API:**
|
|
||||||
```powershell
|
|
||||||
python main.py
|
python main.py
|
||||||
```
|
```
|
||||||
|
|
||||||
The API will start on `http://localhost:8000`
|
The API starts on `http://localhost:8000`
|
||||||
|
|
||||||
|
### Web Interface
|
||||||
|
|
||||||
|
Open `http://localhost:8000/static/index.html` for the full-featured web interface.
|
||||||
|
|
||||||
## 📚 API Documentation
|
## 📚 API Documentation
|
||||||
|
|
||||||
Once the server is running, visit:
|
|
||||||
- **Swagger UI**: http://localhost:8000/docs
|
- **Swagger UI**: http://localhost:8000/docs
|
||||||
- **ReDoc**: http://localhost:8000/redoc
|
- **ReDoc**: http://localhost:8000/redoc
|
||||||
|
|
||||||
## 🔧 API Endpoints
|
## 🔧 API Endpoints
|
||||||
|
|
||||||
### POST /translate
|
### POST /translate
|
||||||
Translate a single document
|
Translate a document with full customization.
|
||||||
|
|
||||||
**Request:**
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST "http://localhost:8000/translate" \
|
curl -X POST "http://localhost:8000/translate" \
|
||||||
-F "file=@document.xlsx" \
|
-F "file=@document.xlsx" \
|
||||||
-F "target_language=es" \
|
-F "target_language=en" \
|
||||||
-F "source_language=auto"
|
-F "provider=ollama" \
|
||||||
|
-F "ollama_model=gemma3:12b" \
|
||||||
|
-F "translate_images=true" \
|
||||||
|
-F "system_prompt=You are translating HVAC documents. Use: batterie=coil, CTA=AHU"
|
||||||
```
|
```
|
||||||
|
|
||||||
**Response:**
|
### Parameters
|
||||||
Returns the translated document file
|
|
||||||
|
|
||||||
### POST /translate-batch
|
| Parameter | Type | Default | Description |
|
||||||
Translate multiple documents at once
|
|-----------|------|---------|-------------|
|
||||||
|
| `file` | File | required | Document to translate (.xlsx, .docx, .pptx) |
|
||||||
|
| `target_language` | string | required | Target language code (en, fr, es, fa, etc.) |
|
||||||
|
| `provider` | string | google | Translation provider (google, ollama, webllm, deepl, libre) |
|
||||||
|
| `ollama_model` | string | llama3.2 | Ollama model name |
|
||||||
|
| `translate_images` | bool | false | Extract and translate image text with vision |
|
||||||
|
| `system_prompt` | string | "" | Custom instructions and glossary for LLM |
|
||||||
|
|
||||||
**Request:**
|
### GET /ollama/models
|
||||||
```bash
|
List available Ollama models.
|
||||||
curl -X POST "http://localhost:8000/translate-batch" \
|
|
||||||
-F "files=@document1.docx" \
|
|
||||||
-F "files=@document2.pptx" \
|
|
||||||
-F "target_language=fr"
|
|
||||||
```
|
|
||||||
|
|
||||||
### GET /languages
|
### POST /ollama/configure
|
||||||
Get list of supported language codes
|
Configure Ollama settings.
|
||||||
|
|
||||||
### GET /health
|
### GET /health
|
||||||
Health check endpoint
|
Health check endpoint.
|
||||||
|
|
||||||
## 💻 Usage Examples
|
|
||||||
|
|
||||||
### Python Example
|
|
||||||
|
|
||||||
```python
|
|
||||||
import requests
|
|
||||||
|
|
||||||
# Translate a document
|
|
||||||
with open('document.xlsx', 'rb') as f:
|
|
||||||
files = {'file': f}
|
|
||||||
data = {
|
|
||||||
'target_language': 'es',
|
|
||||||
'source_language': 'auto'
|
|
||||||
}
|
|
||||||
response = requests.post('http://localhost:8000/translate', files=files, data=data)
|
|
||||||
|
|
||||||
# Save translated file
|
|
||||||
with open('translated_document.xlsx', 'wb') as output:
|
|
||||||
output.write(response.content)
|
|
||||||
```
|
|
||||||
|
|
||||||
### JavaScript/TypeScript Example
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('file', fileInput.files[0]);
|
|
||||||
formData.append('target_language', 'fr');
|
|
||||||
formData.append('source_language', 'auto');
|
|
||||||
|
|
||||||
const response = await fetch('http://localhost:8000/translate', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
});
|
|
||||||
|
|
||||||
const blob = await response.blob();
|
|
||||||
const url = window.URL.createObjectURL(blob);
|
|
||||||
const a = document.createElement('a');
|
|
||||||
a.href = url;
|
|
||||||
a.download = 'translated_document.docx';
|
|
||||||
a.click();
|
|
||||||
```
|
|
||||||
|
|
||||||
### PowerShell Example
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
$file = Get-Item "document.pptx"
|
|
||||||
$uri = "http://localhost:8000/translate"
|
|
||||||
|
|
||||||
$form = @{
|
|
||||||
file = $file
|
|
||||||
target_language = "de"
|
|
||||||
source_language = "auto"
|
|
||||||
}
|
|
||||||
|
|
||||||
Invoke-RestMethod -Uri $uri -Method Post -Form $form -OutFile "translated_document.pptx"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🌐 Supported Languages
|
## 🌐 Supported Languages
|
||||||
|
|
||||||
The API supports 25+ languages including:
|
| Code | Language | Code | Language |
|
||||||
- Spanish (es), French (fr), German (de)
|
|------|----------|------|----------|
|
||||||
- Italian (it), Portuguese (pt), Russian (ru)
|
| en | English | fr | French |
|
||||||
- Chinese (zh), Japanese (ja), Korean (ko)
|
| fa | Persian/Farsi | es | Spanish |
|
||||||
- Arabic (ar), Hindi (hi), Dutch (nl)
|
| de | German | it | Italian |
|
||||||
- And many more...
|
| pt | Portuguese | ru | Russian |
|
||||||
|
| zh | Chinese | ja | Japanese |
|
||||||
Full list available at: `GET /languages`
|
| ko | Korean | ar | Arabic |
|
||||||
|
|
||||||
## ⚙️ Configuration
|
## ⚙️ Configuration
|
||||||
|
|
||||||
Edit `.env` file to configure:
|
### Environment Variables (.env)
|
||||||
|
|
||||||
```env
|
```env
|
||||||
# Translation Service (google, deepl, libre)
|
# Translation Service
|
||||||
TRANSLATION_SERVICE=google
|
TRANSLATION_SERVICE=google
|
||||||
|
|
||||||
# DeepL API Key (if using DeepL)
|
# Ollama Configuration
|
||||||
|
OLLAMA_BASE_URL=http://localhost:11434
|
||||||
|
OLLAMA_MODEL=llama3.2
|
||||||
|
|
||||||
|
# DeepL API Key (optional)
|
||||||
DEEPL_API_KEY=your_api_key_here
|
DEEPL_API_KEY=your_api_key_here
|
||||||
|
|
||||||
# File Upload Limits
|
# File Limits
|
||||||
MAX_FILE_SIZE_MB=50
|
MAX_FILE_SIZE_MB=50
|
||||||
|
|
||||||
# Directory Configuration
|
# Directories
|
||||||
UPLOAD_DIR=./uploads
|
UPLOAD_DIR=./uploads
|
||||||
OUTPUT_DIR=./outputs
|
OUTPUT_DIR=./outputs
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🔌 Model Context Protocol (MCP) Integration
|
### Ollama Setup
|
||||||
|
|
||||||
This API is designed to be easily wrapped as an MCP server for future integration with AI assistants and tools.
|
```bash
|
||||||
|
# Install Ollama (Windows)
|
||||||
|
winget install Ollama.Ollama
|
||||||
|
|
||||||
### MCP Server Structure (Future Implementation)
|
# Pull a model
|
||||||
|
ollama pull llama3.2
|
||||||
|
|
||||||
|
# For vision/image translation
|
||||||
|
ollama pull gemma3:12b
|
||||||
|
# or
|
||||||
|
ollama pull qwen3-vl:8b
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 Using System Prompts & Glossary
|
||||||
|
|
||||||
|
### Example: HVAC Translation
|
||||||
|
|
||||||
|
**System Prompt:**
|
||||||
|
```
|
||||||
|
You are translating HVAC technical documents.
|
||||||
|
Use precise technical terminology.
|
||||||
|
Keep unit measurements (kW, m³/h, Pa) unchanged.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Glossary:**
|
||||||
|
```
|
||||||
|
batterie=coil
|
||||||
|
groupe froid=chiller
|
||||||
|
CTA=AHU (Air Handling Unit)
|
||||||
|
échangeur=heat exchanger
|
||||||
|
vanne 3 voies=3-way valve
|
||||||
|
```
|
||||||
|
|
||||||
|
### Presets Available
|
||||||
|
- 🔧 **HVAC**: Heating, Ventilation, Air Conditioning
|
||||||
|
- 💻 **IT**: Software and technology
|
||||||
|
- ⚖️ **Legal**: Legal documents
|
||||||
|
- 🏥 **Medical**: Healthcare terminology
|
||||||
|
|
||||||
|
## 🔌 MCP Integration
|
||||||
|
|
||||||
|
This API can be used as an MCP (Model Context Protocol) server for AI assistants.
|
||||||
|
|
||||||
|
### VS Code Configuration
|
||||||
|
|
||||||
|
Add to your VS Code `settings.json` or `.vscode/mcp.json`:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"mcpServers": {
|
"servers": {
|
||||||
"document-translator": {
|
"document-translator": {
|
||||||
|
"type": "stdio",
|
||||||
"command": "python",
|
"command": "python",
|
||||||
"args": ["-m", "mcp_server"],
|
"args": ["mcp_server.py"],
|
||||||
|
"cwd": "D:/Translate",
|
||||||
"env": {
|
"env": {
|
||||||
"API_URL": "http://localhost:8000"
|
"PYTHONPATH": "D:/Translate"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Example MCP Tools
|
### MCP Tools Available
|
||||||
|
|
||||||
The MCP wrapper will expose these tools:
|
| Tool | Description |
|
||||||
|
|------|-------------|
|
||||||
1. **translate_document** - Translate a single document
|
| `translate_document` | Translate a document file |
|
||||||
2. **translate_batch** - Translate multiple documents
|
| `list_ollama_models` | Get available Ollama models |
|
||||||
3. **get_supported_languages** - List supported languages
|
| `get_supported_languages` | List supported language codes |
|
||||||
4. **check_translation_status** - Check status of translation
|
| `configure_translation` | Set translation provider and options |
|
||||||
|
|
||||||
## 🏗️ Project Structure
|
## 🏗️ Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
Translate/
|
Translate/
|
||||||
├── main.py # FastAPI application
|
├── main.py # FastAPI application
|
||||||
├── config.py # Configuration management
|
├── config.py # Configuration
|
||||||
├── requirements.txt # Dependencies
|
├── requirements.txt # Dependencies
|
||||||
├── .env.example # Environment template
|
├── mcp_server.py # MCP server implementation
|
||||||
├── services/
|
├── services/
|
||||||
│ ├── __init__.py
|
│ └── translation_service.py # Translation providers
|
||||||
│ └── translation_service.py # Translation abstraction layer
|
|
||||||
├── translators/
|
├── translators/
|
||||||
│ ├── __init__.py
|
│ ├── excel_translator.py # Excel with image support
|
||||||
│ ├── excel_translator.py # Excel translation logic
|
│ ├── word_translator.py # Word with image support
|
||||||
│ ├── word_translator.py # Word translation logic
|
│ └── pptx_translator.py # PowerPoint with image support
|
||||||
│ └── pptx_translator.py # PowerPoint translation logic
|
|
||||||
├── utils/
|
├── utils/
|
||||||
│ ├── __init__.py
|
│ ├── file_handler.py # File operations
|
||||||
│ ├── file_handler.py # File operations
|
│ └── exceptions.py # Custom exceptions
|
||||||
│ └── exceptions.py # Custom exceptions
|
├── static/
|
||||||
├── uploads/ # Temporary upload storage
|
│ └── index.html # Web interface
|
||||||
└── outputs/ # Translated files
|
├── uploads/ # Temporary uploads
|
||||||
|
└── outputs/ # Translated files
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🧪 Testing
|
## 🧪 Testing
|
||||||
|
|
||||||
### Manual Testing
|
1. Start the API: `python main.py`
|
||||||
|
2. Open: http://localhost:8000/static/index.html
|
||||||
|
3. Configure Ollama model
|
||||||
|
4. Upload a document
|
||||||
|
5. Select target language and provider
|
||||||
|
6. Click "Translate Document"
|
||||||
|
|
||||||
1. Start the API server
|
## 🛠️ Tech Stack
|
||||||
2. Navigate to http://localhost:8000/docs
|
|
||||||
3. Use the interactive Swagger UI to test endpoints
|
|
||||||
|
|
||||||
### Test Files
|
- **FastAPI**: Modern async web framework
|
||||||
|
- **openpyxl**: Excel manipulation
|
||||||
Prepare test files with:
|
- **python-docx**: Word documents
|
||||||
- Complex formatting (multiple fonts, colors, styles)
|
- **python-pptx**: PowerPoint presentations
|
||||||
- Embedded images and media
|
- **deep-translator**: Google/DeepL/Libre translation
|
||||||
- Tables and merged cells
|
- **requests**: Ollama API communication
|
||||||
- Formulas (for Excel)
|
- **Uvicorn**: ASGI server
|
||||||
- Multiple sections/slides
|
|
||||||
|
|
||||||
## 🛠️ Technical Details
|
|
||||||
|
|
||||||
### Libraries Used
|
|
||||||
|
|
||||||
- **FastAPI**: Modern web framework for building APIs
|
|
||||||
- **openpyxl**: Excel file manipulation with formatting preservation
|
|
||||||
- **python-docx**: Word document handling
|
|
||||||
- **python-pptx**: PowerPoint presentation processing
|
|
||||||
- **deep-translator**: Multi-provider translation service
|
|
||||||
- **Uvicorn**: ASGI server for running FastAPI
|
|
||||||
|
|
||||||
### Design Principles
|
|
||||||
|
|
||||||
1. **Modular Architecture**: Each file type has its own translator module
|
|
||||||
2. **Provider Abstraction**: Easy to swap translation services (Google, DeepL, LibreTranslate)
|
|
||||||
3. **Format Preservation**: All translators maintain original document structure
|
|
||||||
4. **Error Handling**: Comprehensive error handling and logging
|
|
||||||
5. **Scalability**: Ready for MCP integration and microservices architecture
|
|
||||||
|
|
||||||
## 🔐 Security Considerations
|
|
||||||
|
|
||||||
For production deployment:
|
|
||||||
|
|
||||||
1. **Configure CORS** properly in `main.py`
|
|
||||||
2. **Add authentication** for API endpoints
|
|
||||||
3. **Implement rate limiting** to prevent abuse
|
|
||||||
4. **Use HTTPS** for secure file transmission
|
|
||||||
5. **Sanitize file uploads** to prevent malicious files
|
|
||||||
6. **Set appropriate file size limits**
|
|
||||||
|
|
||||||
## 📝 License
|
## 📝 License
|
||||||
|
|
||||||
MIT License - Feel free to use this project for your needs.
|
MIT License
|
||||||
|
|
||||||
## 🤝 Contributing
|
## 🤝 Contributing
|
||||||
|
|
||||||
Contributions are welcome! Please feel free to submit a Pull Request.
|
Contributions welcome! Please submit a Pull Request.
|
||||||
|
|
||||||
## 📧 Support
|
|
||||||
|
|
||||||
For issues and questions, please open an issue on the repository.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Built with ❤️ using Python and FastAPI**
|
**Built with ❤️ using Python, FastAPI, and Ollama**
|
||||||
|
|||||||
157
mcp.json
Normal file
157
mcp.json
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/mcp-config.json",
|
||||||
|
"name": "document-translator",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Document Translation API - Translate Excel, Word, PowerPoint files with format preservation",
|
||||||
|
"author": "Sepehr",
|
||||||
|
"repository": "https://gitea.parsanet.org/sepehr/office_translator.git",
|
||||||
|
"license": "MIT",
|
||||||
|
|
||||||
|
"runtime": {
|
||||||
|
"type": "python",
|
||||||
|
"command": "python",
|
||||||
|
"args": ["mcp_server.py"],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
|
||||||
|
"requirements": {
|
||||||
|
"python": ">=3.8",
|
||||||
|
"dependencies": [
|
||||||
|
"requests>=2.28.0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"tools": [
|
||||||
|
{
|
||||||
|
"name": "translate_document",
|
||||||
|
"description": "Translate a document (Excel, Word, PowerPoint) to another language while preserving all formatting, styles, formulas, and layouts",
|
||||||
|
"parameters": {
|
||||||
|
"file_path": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Absolute path to the document file (.xlsx, .docx, .pptx)",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"target_language": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Target language code (en, fr, es, fa, de, it, pt, ru, zh, ja, ko, ar)",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"provider": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["google", "ollama", "deepl", "libre"],
|
||||||
|
"default": "google",
|
||||||
|
"description": "Translation provider to use"
|
||||||
|
},
|
||||||
|
"ollama_model": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Ollama model name (e.g., llama3.2, gemma3:12b, qwen3-vl)"
|
||||||
|
},
|
||||||
|
"translate_images": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Use vision model to extract and translate text from embedded images"
|
||||||
|
},
|
||||||
|
"system_prompt": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Custom instructions and context for LLM translation (glossary, domain context, style guidelines)"
|
||||||
|
},
|
||||||
|
"output_path": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path where to save the translated document"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"examples": [
|
||||||
|
{
|
||||||
|
"description": "Translate Excel file to French using Google",
|
||||||
|
"arguments": {
|
||||||
|
"file_path": "C:/Documents/data.xlsx",
|
||||||
|
"target_language": "fr",
|
||||||
|
"provider": "google"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Translate Word document to Persian with Ollama and custom glossary",
|
||||||
|
"arguments": {
|
||||||
|
"file_path": "C:/Documents/report.docx",
|
||||||
|
"target_language": "fa",
|
||||||
|
"provider": "ollama",
|
||||||
|
"ollama_model": "gemma3:12b",
|
||||||
|
"system_prompt": "You are translating HVAC technical documentation. Glossary: batterie=کویل, ventilateur=فن, condenseur=کندانسور"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Translate PowerPoint with image text extraction",
|
||||||
|
"arguments": {
|
||||||
|
"file_path": "C:/Presentations/slides.pptx",
|
||||||
|
"target_language": "de",
|
||||||
|
"provider": "ollama",
|
||||||
|
"ollama_model": "gemma3:12b",
|
||||||
|
"translate_images": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "list_ollama_models",
|
||||||
|
"description": "List all available Ollama models for translation",
|
||||||
|
"parameters": {
|
||||||
|
"base_url": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "http://localhost:11434",
|
||||||
|
"description": "Ollama server URL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "get_supported_languages",
|
||||||
|
"description": "Get the full list of supported language codes and names",
|
||||||
|
"parameters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "check_api_health",
|
||||||
|
"description": "Check if the translation API server is running and healthy",
|
||||||
|
"parameters": {}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"features": [
|
||||||
|
"Format-preserving translation for Excel, Word, PowerPoint",
|
||||||
|
"Multiple translation providers (Google, Ollama, DeepL, LibreTranslate)",
|
||||||
|
"Image text extraction using vision models (Gemma3, Qwen3-VL)",
|
||||||
|
"Custom system prompts and glossaries for technical translation",
|
||||||
|
"Domain-specific presets (HVAC, IT, Legal, Medical)",
|
||||||
|
"Browser-based WebLLM support for offline translation"
|
||||||
|
],
|
||||||
|
|
||||||
|
"usage": {
|
||||||
|
"start_server": "python main.py",
|
||||||
|
"api_endpoint": "http://localhost:8000",
|
||||||
|
"web_interface": "http://localhost:8000"
|
||||||
|
},
|
||||||
|
|
||||||
|
"providers": {
|
||||||
|
"google": {
|
||||||
|
"description": "Google Translate (free, no API key required)",
|
||||||
|
"supports_system_prompt": false
|
||||||
|
},
|
||||||
|
"ollama": {
|
||||||
|
"description": "Local Ollama LLM server",
|
||||||
|
"supports_system_prompt": true,
|
||||||
|
"supports_vision": true,
|
||||||
|
"recommended_models": [
|
||||||
|
"llama3.2",
|
||||||
|
"gemma3:12b",
|
||||||
|
"qwen3-vl",
|
||||||
|
"mistral"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"deepl": {
|
||||||
|
"description": "DeepL API (requires API key)",
|
||||||
|
"supports_system_prompt": false
|
||||||
|
},
|
||||||
|
"libre": {
|
||||||
|
"description": "LibreTranslate (self-hosted)",
|
||||||
|
"supports_system_prompt": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
391
mcp_server.py
Normal file
391
mcp_server.py
Normal file
@ -0,0 +1,391 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
MCP Server for Document Translation API
|
||||||
|
Model Context Protocol server for AI assistant integration
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import asyncio
|
||||||
|
import base64
|
||||||
|
import requests
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
# MCP Protocol Constants
|
||||||
|
JSONRPC_VERSION = "2.0"
|
||||||
|
|
||||||
|
class MCPServer:
|
||||||
|
"""MCP Server for Document Translation"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.api_base = "http://localhost:8000"
|
||||||
|
self.capabilities = {
|
||||||
|
"tools": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_tools(self) -> list:
|
||||||
|
"""Return list of available tools"""
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"name": "translate_document",
|
||||||
|
"description": "Translate a document (Excel, Word, PowerPoint) to another language while preserving formatting",
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"file_path": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path to the document file (.xlsx, .docx, .pptx)"
|
||||||
|
},
|
||||||
|
"target_language": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Target language code (e.g., 'en', 'fr', 'es', 'fa', 'de')"
|
||||||
|
},
|
||||||
|
"provider": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["google", "ollama", "deepl", "libre"],
|
||||||
|
"description": "Translation provider (default: google)"
|
||||||
|
},
|
||||||
|
"ollama_model": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Ollama model to use (e.g., 'llama3.2', 'gemma3:12b')"
|
||||||
|
},
|
||||||
|
"translate_images": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Extract and translate text from images using vision model"
|
||||||
|
},
|
||||||
|
"system_prompt": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Custom system prompt with context, glossary, or instructions for LLM translation"
|
||||||
|
},
|
||||||
|
"output_path": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path where to save the translated document (optional)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["file_path", "target_language"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "list_ollama_models",
|
||||||
|
"description": "List available Ollama models for translation",
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"base_url": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Ollama server URL (default: http://localhost:11434)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "get_supported_languages",
|
||||||
|
"description": "Get list of supported language codes for translation",
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "configure_translation",
|
||||||
|
"description": "Configure translation settings",
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"provider": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["google", "ollama", "deepl", "libre"],
|
||||||
|
"description": "Default translation provider"
|
||||||
|
},
|
||||||
|
"ollama_url": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Ollama server URL"
|
||||||
|
},
|
||||||
|
"ollama_model": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Default Ollama model"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "check_api_health",
|
||||||
|
"description": "Check if the translation API is running and healthy",
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
async def handle_tool_call(self, name: str, arguments: dict) -> dict:
|
||||||
|
"""Handle tool calls"""
|
||||||
|
try:
|
||||||
|
if name == "translate_document":
|
||||||
|
return await self.translate_document(arguments)
|
||||||
|
elif name == "list_ollama_models":
|
||||||
|
return await self.list_ollama_models(arguments)
|
||||||
|
elif name == "get_supported_languages":
|
||||||
|
return await self.get_supported_languages()
|
||||||
|
elif name == "configure_translation":
|
||||||
|
return await self.configure_translation(arguments)
|
||||||
|
elif name == "check_api_health":
|
||||||
|
return await self.check_api_health()
|
||||||
|
else:
|
||||||
|
return {"error": f"Unknown tool: {name}"}
|
||||||
|
except Exception as e:
|
||||||
|
return {"error": str(e)}
|
||||||
|
|
||||||
|
async def translate_document(self, args: dict) -> dict:
|
||||||
|
"""Translate a document file"""
|
||||||
|
file_path = Path(args["file_path"])
|
||||||
|
|
||||||
|
if not file_path.exists():
|
||||||
|
return {"error": f"File not found: {file_path}"}
|
||||||
|
|
||||||
|
# Prepare form data
|
||||||
|
with open(file_path, 'rb') as f:
|
||||||
|
files = {'file': (file_path.name, f)}
|
||||||
|
data = {
|
||||||
|
'target_language': args['target_language'],
|
||||||
|
'provider': args.get('provider', 'google'),
|
||||||
|
'translate_images': str(args.get('translate_images', False)).lower(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.get('ollama_model'):
|
||||||
|
data['ollama_model'] = args['ollama_model']
|
||||||
|
|
||||||
|
if args.get('system_prompt'):
|
||||||
|
data['system_prompt'] = args['system_prompt']
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(
|
||||||
|
f"{self.api_base}/translate",
|
||||||
|
files=files,
|
||||||
|
data=data,
|
||||||
|
timeout=300 # 5 minutes timeout
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
# Save translated file
|
||||||
|
output_path = args.get('output_path')
|
||||||
|
if not output_path:
|
||||||
|
output_path = file_path.parent / f"translated_{file_path.name}"
|
||||||
|
|
||||||
|
output_path = Path(output_path)
|
||||||
|
with open(output_path, 'wb') as out:
|
||||||
|
out.write(response.content)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"message": f"Document translated successfully",
|
||||||
|
"output_path": str(output_path),
|
||||||
|
"source_file": str(file_path),
|
||||||
|
"target_language": args['target_language'],
|
||||||
|
"provider": args.get('provider', 'google')
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
error_detail = response.json() if response.headers.get('content-type') == 'application/json' else response.text
|
||||||
|
return {"error": f"Translation failed: {error_detail}"}
|
||||||
|
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
return {"error": "Cannot connect to translation API. Make sure the server is running on http://localhost:8000"}
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
return {"error": "Translation request timed out"}
|
||||||
|
|
||||||
|
async def list_ollama_models(self, args: dict) -> dict:
|
||||||
|
"""List available Ollama models"""
|
||||||
|
base_url = args.get('base_url', 'http://localhost:11434')
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(
|
||||||
|
f"{self.api_base}/ollama/models",
|
||||||
|
params={'base_url': base_url},
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
return {
|
||||||
|
"models": data.get('models', []),
|
||||||
|
"count": data.get('count', 0),
|
||||||
|
"ollama_url": base_url
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {"error": "Failed to list models", "models": []}
|
||||||
|
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
return {"error": "Cannot connect to API server", "models": []}
|
||||||
|
|
||||||
|
async def get_supported_languages(self) -> dict:
|
||||||
|
"""Get supported language codes"""
|
||||||
|
return {
|
||||||
|
"languages": [
|
||||||
|
{"code": "en", "name": "English"},
|
||||||
|
{"code": "fa", "name": "Persian/Farsi"},
|
||||||
|
{"code": "fr", "name": "French"},
|
||||||
|
{"code": "es", "name": "Spanish"},
|
||||||
|
{"code": "de", "name": "German"},
|
||||||
|
{"code": "it", "name": "Italian"},
|
||||||
|
{"code": "pt", "name": "Portuguese"},
|
||||||
|
{"code": "ru", "name": "Russian"},
|
||||||
|
{"code": "zh", "name": "Chinese"},
|
||||||
|
{"code": "ja", "name": "Japanese"},
|
||||||
|
{"code": "ko", "name": "Korean"},
|
||||||
|
{"code": "ar", "name": "Arabic"},
|
||||||
|
{"code": "nl", "name": "Dutch"},
|
||||||
|
{"code": "pl", "name": "Polish"},
|
||||||
|
{"code": "tr", "name": "Turkish"},
|
||||||
|
{"code": "vi", "name": "Vietnamese"},
|
||||||
|
{"code": "th", "name": "Thai"},
|
||||||
|
{"code": "hi", "name": "Hindi"},
|
||||||
|
{"code": "he", "name": "Hebrew"},
|
||||||
|
{"code": "sv", "name": "Swedish"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
async def configure_translation(self, args: dict) -> dict:
|
||||||
|
"""Configure translation settings"""
|
||||||
|
config = {}
|
||||||
|
|
||||||
|
if args.get('ollama_url') and args.get('ollama_model'):
|
||||||
|
try:
|
||||||
|
response = requests.post(
|
||||||
|
f"{self.api_base}/ollama/configure",
|
||||||
|
data={
|
||||||
|
'base_url': args['ollama_url'],
|
||||||
|
'model': args['ollama_model']
|
||||||
|
},
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
config['ollama'] = response.json()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
config['ollama_error'] = str(e)
|
||||||
|
|
||||||
|
config['provider'] = args.get('provider', 'google')
|
||||||
|
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"configuration": config
|
||||||
|
}
|
||||||
|
|
||||||
|
async def check_api_health(self) -> dict:
|
||||||
|
"""Check API health status"""
|
||||||
|
try:
|
||||||
|
response = requests.get(f"{self.api_base}/health", timeout=5)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
return {
|
||||||
|
"status": "healthy",
|
||||||
|
"api_url": self.api_base,
|
||||||
|
"details": response.json()
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {"status": "unhealthy", "error": "API returned non-200 status"}
|
||||||
|
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
return {
|
||||||
|
"status": "unavailable",
|
||||||
|
"error": "Cannot connect to API. Start the server with: python main.py"
|
||||||
|
}
|
||||||
|
|
||||||
|
def create_response(self, id: Any, result: Any) -> dict:
|
||||||
|
"""Create JSON-RPC response"""
|
||||||
|
return {
|
||||||
|
"jsonrpc": JSONRPC_VERSION,
|
||||||
|
"id": id,
|
||||||
|
"result": result
|
||||||
|
}
|
||||||
|
|
||||||
|
def create_error(self, id: Any, code: int, message: str) -> dict:
|
||||||
|
"""Create JSON-RPC error response"""
|
||||||
|
return {
|
||||||
|
"jsonrpc": JSONRPC_VERSION,
|
||||||
|
"id": id,
|
||||||
|
"error": {
|
||||||
|
"code": code,
|
||||||
|
"message": message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async def handle_message(self, message: dict) -> Optional[dict]:
|
||||||
|
"""Handle incoming JSON-RPC message"""
|
||||||
|
msg_id = message.get("id")
|
||||||
|
method = message.get("method")
|
||||||
|
params = message.get("params", {})
|
||||||
|
|
||||||
|
if method == "initialize":
|
||||||
|
return self.create_response(msg_id, {
|
||||||
|
"protocolVersion": "2024-11-05",
|
||||||
|
"capabilities": self.capabilities,
|
||||||
|
"serverInfo": {
|
||||||
|
"name": "document-translator",
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
elif method == "notifications/initialized":
|
||||||
|
return None # No response needed for notifications
|
||||||
|
|
||||||
|
elif method == "tools/list":
|
||||||
|
return self.create_response(msg_id, {
|
||||||
|
"tools": self.get_tools()
|
||||||
|
})
|
||||||
|
|
||||||
|
elif method == "tools/call":
|
||||||
|
tool_name = params.get("name")
|
||||||
|
tool_args = params.get("arguments", {})
|
||||||
|
result = await self.handle_tool_call(tool_name, tool_args)
|
||||||
|
|
||||||
|
return self.create_response(msg_id, {
|
||||||
|
"content": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"text": json.dumps(result, indent=2, ensure_ascii=False)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
elif method == "ping":
|
||||||
|
return self.create_response(msg_id, {})
|
||||||
|
|
||||||
|
else:
|
||||||
|
return self.create_error(msg_id, -32601, f"Method not found: {method}")
|
||||||
|
|
||||||
|
async def run(self):
|
||||||
|
"""Run the MCP server using stdio"""
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
line = sys.stdin.readline()
|
||||||
|
if not line:
|
||||||
|
break
|
||||||
|
|
||||||
|
message = json.loads(line)
|
||||||
|
response = await self.handle_message(message)
|
||||||
|
|
||||||
|
if response:
|
||||||
|
sys.stdout.write(json.dumps(response) + "\n")
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
error = self.create_error(None, -32700, f"Parse error: {e}")
|
||||||
|
sys.stdout.write(json.dumps(error) + "\n")
|
||||||
|
sys.stdout.flush()
|
||||||
|
except Exception as e:
|
||||||
|
error = self.create_error(None, -32603, f"Internal error: {e}")
|
||||||
|
sys.stdout.write(json.dumps(error) + "\n")
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Main entry point"""
|
||||||
|
server = MCPServer()
|
||||||
|
asyncio.run(server.run())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
x
Reference in New Issue
Block a user