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.
|
||||
|
||||
## 🎯 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
|
||||
- ✅ Preserves cell merging
|
||||
- ✅ Maintains font styles (size, bold, italic, color)
|
||||
- ✅ Keeps background colors and borders
|
||||
- ✅ Translates text within formulas while preserving formula structure
|
||||
- ✅ Retains embedded images in original positions
|
||||
- ✅ Preserves cell merging, formulas, and styles
|
||||
- ✅ Maintains font styles, colors, and borders
|
||||
- ✅ Image text extraction with vision models
|
||||
- ✅ Adds translated image text as comments
|
||||
|
||||
### Word Translation (.docx)
|
||||
### 📝 Word Translation (.docx)
|
||||
- ✅ Translates body text, headers, footers, and tables
|
||||
- ✅ Preserves heading styles and paragraph formatting
|
||||
- ✅ Maintains lists (numbered/bulleted)
|
||||
- ✅ Keeps embedded images, charts, and SmartArt in place
|
||||
- ✅ Preserves table structures and cell formatting
|
||||
- ✅ Maintains lists, images, charts, and SmartArt
|
||||
- ✅ Image text extraction and translation
|
||||
|
||||
### PowerPoint Translation (.pptx)
|
||||
### 📽️ PowerPoint Translation (.pptx)
|
||||
- ✅ Translates slide titles, body text, and speaker notes
|
||||
- ✅ Preserves slide layouts and transitions
|
||||
- ✅ Maintains animations
|
||||
- ✅ Keeps images, videos, and shapes in exact positions
|
||||
- ✅ Preserves layering order
|
||||
- ✅ Preserves slide layouts, transitions, and animations
|
||||
- ✅ Image text extraction with text boxes added below images
|
||||
- ✅ Keeps layering order and positions
|
||||
|
||||
### 🧠 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
|
||||
|
||||
### Installation
|
||||
|
||||
1. **Clone the repository:**
|
||||
```powershell
|
||||
git clone <repository-url>
|
||||
cd Translate
|
||||
```
|
||||
# Clone the repository
|
||||
git clone https://gitea.parsanet.org/sepehr/office_translator.git
|
||||
cd office_translator
|
||||
|
||||
2. **Create a virtual environment:**
|
||||
```powershell
|
||||
# Create virtual environment
|
||||
python -m venv venv
|
||||
.\venv\Scripts\Activate.ps1
|
||||
```
|
||||
|
||||
3. **Install dependencies:**
|
||||
```powershell
|
||||
# Install dependencies
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
4. **Configure environment:**
|
||||
```powershell
|
||||
cp .env.example .env
|
||||
# Edit .env with your preferred settings
|
||||
```
|
||||
|
||||
5. **Run the API:**
|
||||
```powershell
|
||||
# Run the API
|
||||
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
|
||||
|
||||
Once the server is running, visit:
|
||||
- **Swagger UI**: http://localhost:8000/docs
|
||||
- **ReDoc**: http://localhost:8000/redoc
|
||||
|
||||
## 🔧 API Endpoints
|
||||
|
||||
### POST /translate
|
||||
Translate a single document
|
||||
Translate a document with full customization.
|
||||
|
||||
**Request:**
|
||||
```bash
|
||||
curl -X POST "http://localhost:8000/translate" \
|
||||
-F "file=@document.xlsx" \
|
||||
-F "target_language=es" \
|
||||
-F "source_language=auto"
|
||||
-F "target_language=en" \
|
||||
-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:**
|
||||
Returns the translated document file
|
||||
### Parameters
|
||||
|
||||
### POST /translate-batch
|
||||
Translate multiple documents at once
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
| `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:**
|
||||
```bash
|
||||
curl -X POST "http://localhost:8000/translate-batch" \
|
||||
-F "files=@document1.docx" \
|
||||
-F "files=@document2.pptx" \
|
||||
-F "target_language=fr"
|
||||
```
|
||||
### GET /ollama/models
|
||||
List available Ollama models.
|
||||
|
||||
### GET /languages
|
||||
Get list of supported language codes
|
||||
### POST /ollama/configure
|
||||
Configure Ollama settings.
|
||||
|
||||
### GET /health
|
||||
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"
|
||||
```
|
||||
Health check endpoint.
|
||||
|
||||
## 🌐 Supported Languages
|
||||
|
||||
The API supports 25+ languages including:
|
||||
- Spanish (es), French (fr), German (de)
|
||||
- Italian (it), Portuguese (pt), Russian (ru)
|
||||
- Chinese (zh), Japanese (ja), Korean (ko)
|
||||
- Arabic (ar), Hindi (hi), Dutch (nl)
|
||||
- And many more...
|
||||
|
||||
Full list available at: `GET /languages`
|
||||
| Code | Language | Code | Language |
|
||||
|------|----------|------|----------|
|
||||
| en | English | fr | French |
|
||||
| fa | Persian/Farsi | es | Spanish |
|
||||
| de | German | it | Italian |
|
||||
| pt | Portuguese | ru | Russian |
|
||||
| zh | Chinese | ja | Japanese |
|
||||
| ko | Korean | ar | Arabic |
|
||||
|
||||
## ⚙️ Configuration
|
||||
|
||||
Edit `.env` file to configure:
|
||||
### Environment Variables (.env)
|
||||
|
||||
```env
|
||||
# Translation Service (google, deepl, libre)
|
||||
# Translation Service
|
||||
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
|
||||
|
||||
# File Upload Limits
|
||||
# File Limits
|
||||
MAX_FILE_SIZE_MB=50
|
||||
|
||||
# Directory Configuration
|
||||
# Directories
|
||||
UPLOAD_DIR=./uploads
|
||||
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
|
||||
{
|
||||
"mcpServers": {
|
||||
"servers": {
|
||||
"document-translator": {
|
||||
"type": "stdio",
|
||||
"command": "python",
|
||||
"args": ["-m", "mcp_server"],
|
||||
"args": ["mcp_server.py"],
|
||||
"cwd": "D:/Translate",
|
||||
"env": {
|
||||
"API_URL": "http://localhost:8000"
|
||||
"PYTHONPATH": "D:/Translate"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Example MCP Tools
|
||||
### MCP Tools Available
|
||||
|
||||
The MCP wrapper will expose these tools:
|
||||
|
||||
1. **translate_document** - Translate a single document
|
||||
2. **translate_batch** - Translate multiple documents
|
||||
3. **get_supported_languages** - List supported languages
|
||||
4. **check_translation_status** - Check status of translation
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `translate_document` | Translate a document file |
|
||||
| `list_ollama_models` | Get available Ollama models |
|
||||
| `get_supported_languages` | List supported language codes |
|
||||
| `configure_translation` | Set translation provider and options |
|
||||
|
||||
## 🏗️ Project Structure
|
||||
|
||||
```
|
||||
Translate/
|
||||
├── main.py # FastAPI application
|
||||
├── config.py # Configuration management
|
||||
├── requirements.txt # Dependencies
|
||||
├── .env.example # Environment template
|
||||
├── main.py # FastAPI application
|
||||
├── config.py # Configuration
|
||||
├── requirements.txt # Dependencies
|
||||
├── mcp_server.py # MCP server implementation
|
||||
├── services/
|
||||
│ ├── __init__.py
|
||||
│ └── translation_service.py # Translation abstraction layer
|
||||
│ └── translation_service.py # Translation providers
|
||||
├── translators/
|
||||
│ ├── __init__.py
|
||||
│ ├── excel_translator.py # Excel translation logic
|
||||
│ ├── word_translator.py # Word translation logic
|
||||
│ └── pptx_translator.py # PowerPoint translation logic
|
||||
│ ├── excel_translator.py # Excel with image support
|
||||
│ ├── word_translator.py # Word with image support
|
||||
│ └── pptx_translator.py # PowerPoint with image support
|
||||
├── utils/
|
||||
│ ├── __init__.py
|
||||
│ ├── file_handler.py # File operations
|
||||
│ └── exceptions.py # Custom exceptions
|
||||
├── uploads/ # Temporary upload storage
|
||||
└── outputs/ # Translated files
|
||||
│ ├── file_handler.py # File operations
|
||||
│ └── exceptions.py # Custom exceptions
|
||||
├── static/
|
||||
│ └── index.html # Web interface
|
||||
├── uploads/ # Temporary uploads
|
||||
└── outputs/ # Translated files
|
||||
```
|
||||
|
||||
## 🧪 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
|
||||
2. Navigate to http://localhost:8000/docs
|
||||
3. Use the interactive Swagger UI to test endpoints
|
||||
## 🛠️ Tech Stack
|
||||
|
||||
### Test Files
|
||||
|
||||
Prepare test files with:
|
||||
- Complex formatting (multiple fonts, colors, styles)
|
||||
- Embedded images and media
|
||||
- Tables and merged cells
|
||||
- Formulas (for Excel)
|
||||
- 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**
|
||||
- **FastAPI**: Modern async web framework
|
||||
- **openpyxl**: Excel manipulation
|
||||
- **python-docx**: Word documents
|
||||
- **python-pptx**: PowerPoint presentations
|
||||
- **deep-translator**: Google/DeepL/Libre translation
|
||||
- **requests**: Ollama API communication
|
||||
- **Uvicorn**: ASGI server
|
||||
|
||||
## 📝 License
|
||||
|
||||
MIT License - Feel free to use this project for your needs.
|
||||
MIT License
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
Contributions are welcome! Please feel free to submit a Pull Request.
|
||||
|
||||
## 📧 Support
|
||||
|
||||
For issues and questions, please open an issue on the repository.
|
||||
Contributions welcome! Please submit a Pull Request.
|
||||
|
||||
---
|
||||
|
||||
**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