From a4ecd3e0eca4acb6287c6a97a4a9201c540a2801 Mon Sep 17 00:00:00 2001 From: Sepehr Date: Sun, 30 Nov 2025 16:53:53 +0100 Subject: [PATCH] Add MCP server and configuration for AI assistant integration --- README.md | 368 +++++++++++++++++++++-------------------------- mcp.json | 157 ++++++++++++++++++++ mcp_server.py | 391 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 714 insertions(+), 202 deletions(-) create mode 100644 mcp.json create mode 100644 mcp_server.py diff --git a/README.md b/README.md index 7e25517..9f9f213 100644 --- a/README.md +++ b/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 -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** diff --git a/mcp.json b/mcp.json new file mode 100644 index 0000000..1f01d4f --- /dev/null +++ b/mcp.json @@ -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 + } + } +} diff --git a/mcp_server.py b/mcp_server.py new file mode 100644 index 0000000..26c3680 --- /dev/null +++ b/mcp_server.py @@ -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()