SimpleLocalize MCP Server
by GalvinGao
from typing import Any, List
import httpx
import os
from mcp.server.fastmcp import FastMCP
# Initialize FastMCP server
mcp = FastMCP("simplelocalize")
# Constants
SIMPLELOCALIZE_API_BASE = "https://api.simplelocalize.io"
SIMPLELOCALIZE_API_KEY = os.getenv("SIMPLELOCALIZE_API_KEY")
if not SIMPLELOCALIZE_API_KEY:
raise ValueError("SIMPLELOCALIZE_API_KEY environment variable is not set")
class SimpleLocalizeError(Exception):
"""Custom error for SimpleLocalize API errors"""
pass
async def make_simplelocalize_request(method: str, endpoint: str, json_data: dict | None = None) -> dict[str, Any]:
"""Make a request to the SimpleLocalize API with proper error handling."""
headers = {
"X-SimpleLocalize-Token": SIMPLELOCALIZE_API_KEY,
"Content-Type": "application/json"
}
url = f"{SIMPLELOCALIZE_API_BASE}{endpoint}"
async with httpx.AsyncClient() as client:
try:
if method.upper() == "POST":
response = await client.post(url, headers=headers, json=json_data, timeout=30.0)
elif method.upper() == "PATCH":
response = await client.patch(url, headers=headers, json=json_data, timeout=30.0)
else:
raise ValueError(f"Unsupported HTTP method: {method}")
response.raise_for_status()
return response.json()
except httpx.HTTPError as e:
raise SimpleLocalizeError(f"SimpleLocalize API error: {str(e)}")
@mcp.tool()
async def create_translation_keys(keys: List[dict]) -> str:
"""Create translation keys in bulk for a project.
This endpoint allows you to create multiple translation keys at once. You can create up to 100 translation keys in a single request.
Each key must have a 'key' field, and optionally can include 'namespace' and 'description' fields.
Args:
keys: List of dictionaries containing key information with fields:
- key (required): Translation key (max 500 chars)
- namespace (optional): Namespace for the key (max 64 chars)
- description (optional): Description for translators (max 500 chars)
"""
# Validate and clean input
cleaned_keys = []
for key_info in keys:
if not key_info.get("key"):
raise ValueError("Each key must have a 'key' field")
cleaned_key = {
"key": key_info["key"]
}
# Only include optional fields if they exist
if "namespace" in key_info:
cleaned_key["namespace"] = key_info["namespace"]
if "description" in key_info:
cleaned_key["description"] = key_info["description"]
cleaned_keys.append(cleaned_key)
if len(cleaned_keys) > 100:
raise ValueError("Maximum 100 keys allowed per request")
try:
result = await make_simplelocalize_request(
"POST",
"/api/v1/translation-keys/bulk",
{"translationKeys": cleaned_keys}
)
if "failures" in result.get("data", {}):
failures = result["data"]["failures"]
if failures:
return f"Some keys failed to create: {failures}"
return f"Successfully created {len(cleaned_keys)} translation keys"
except SimpleLocalizeError as e:
return str(e)
@mcp.tool()
async def update_translations(translations: List[dict]) -> str:
"""Update translations in bulk with a single request.
This endpoint allows you to update multiple translations at once. You can update up to 100 translations in a single request.
Each translation must specify the key, language, and text. Namespace is optional.
Args:
translations: List of dictionaries containing translation information with fields:
- key (required): Translation key
- language (required): Language code
- text (required): Translation text (max 65535 chars)
- namespace (optional): Namespace for the key
"""
# Validate and clean input
cleaned_translations = []
for trans in translations:
if not all(k in trans for k in ["key", "language", "text"]):
raise ValueError("Each translation must have 'key', 'language', and 'text' fields")
cleaned_trans = {
"key": trans["key"],
"language": trans["language"],
"text": trans["text"]
}
# Only include namespace if it exists
if "namespace" in trans:
cleaned_trans["namespace"] = trans["namespace"]
cleaned_translations.append(cleaned_trans)
if len(cleaned_translations) > 100:
raise ValueError("Maximum 100 translations allowed per request")
try:
result = await make_simplelocalize_request(
"PATCH",
"/api/v2/translations/bulk",
{"translations": cleaned_translations}
)
if "failures" in result.get("data", {}):
failures = result["data"]["failures"]
if failures:
return f"Some translations failed to update: {failures}"
return f"Successfully updated {len(cleaned_translations)} translations"
except SimpleLocalizeError as e:
return str(e)
if __name__ == "__main__":
mcp.run(transport='stdio')
ID: 22x90bsxj3