Skip to main content
Glama

DeepL MCP Server

index.ts8.65 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, McpError, ErrorCode, Tool, // Corrected type name CallToolResultSchema, // Corrected type name based on error suggestion ListToolsResultSchema, // Corrected type name based on error suggestion TextContent, } from '@modelcontextprotocol/sdk/types.js'; import axios, { AxiosInstance, AxiosError } from 'axios'; import { z } from 'zod'; // --- Environment Variable Check --- const API_KEY = process.env.DEEPL_API_KEY; if (!API_KEY) { console.error('DEEPL_API_KEY environment variable is not set.'); process.exit(1); // Exit if API key is missing } // --- Input Schemas (using Zod) --- const TranslateTextInputSchema = z.object({ text: z.array(z.string()).min(1, "The 'text' array cannot be empty."), target_lang: z.string(), source_lang: z.string().optional(), }); const ListLanguagesInputSchema = z.object({ type: z.enum(['source', 'target']).optional(), }); // --- DeepL API Response Types --- interface DeepLTranslation { detected_source_language: string; text: string; } interface DeepLLanguage { language: string; name: string; supports_formality: boolean; } // --- DeepL Server Class --- class DeepLServer { private server: Server; private axiosInstance: AxiosInstance; private readonly baseUrl = 'https://api-free.deepl.com'; constructor() { this.server = new Server( { name: 'deepl-mcp-server', version: '0.1.0', }, { capabilities: { // Resources not implemented in this version resources: {}, tools: {}, // Tool handlers are registered below }, } ); this.axiosInstance = axios.create({ baseURL: this.baseUrl, headers: { Authorization: `DeepL-Auth-Key ${API_KEY}`, 'Content-Type': 'application/json', }, }); this.setupToolHandlers(); // Basic error logging for the server itself this.server.onerror = (error) => console.error('[MCP Server Error]', error); process.on('SIGINT', async () => { console.error('Received SIGINT, shutting down server...'); await this.server.close(); process.exit(0); }); } private setupToolHandlers() { // --- ListTools Handler --- this.server.setRequestHandler( ListToolsRequestSchema, async (): Promise<z.infer<typeof ListToolsResultSchema>> => { // Corrected response type const tools: Tool[] = [ // Corrected type name { name: 'translate_text', description: 'Translates one or more text strings using the DeepL API.', inputSchema: { type: 'object', properties: { text: { type: 'array', items: { type: 'string' }, description: 'Text(s) to translate', }, target_lang: { type: 'string', description: 'Target language code (e.g., DE, FR)', }, source_lang: { type: 'string', description: 'Source language code; auto-detected if omitted', }, }, required: ['text', 'target_lang'], }, }, { name: 'list_languages', description: 'Retrieves the list of languages supported by the DeepL API.', inputSchema: { type: 'object', properties: { type: { type: 'string', enum: ['source', 'target'], description: "Filter by 'source' or 'target' languages", }, }, required: [], }, }, ]; return { tools }; } ); // --- CallTool Handler --- this.server.setRequestHandler( CallToolRequestSchema, async (request): Promise<z.infer<typeof CallToolResultSchema>> => { // Corrected response type try { switch (request.params.name) { case 'translate_text': return await this.callTranslateText(request.params.arguments); case 'list_languages': return await this.callListLanguages(request.params.arguments); default: throw new McpError(ErrorCode.MethodNotFound, `Tool '${request.params.name}' not found.`); } } catch (error) { return this.handleToolError(error); } } ); } // --- Specific Tool Implementations --- private async callTranslateText(args: any): Promise<z.infer<typeof CallToolResultSchema>> { // Corrected response type const validatedArgs = TranslateTextInputSchema.parse(args); // Validate input (throws ZodError on failure) const response = await this.axiosInstance.post<{ translations: DeepLTranslation[] }>( '/v2/translate', { text: validatedArgs.text, target_lang: validatedArgs.target_lang, ...(validatedArgs.source_lang && { source_lang: validatedArgs.source_lang }), } ); const content: TextContent = { type: 'text', text: JSON.stringify(response.data.translations, null, 2), // Pretty print JSON mimeType: 'application/json', }; return { content: [content] }; } private async callListLanguages(args: any): Promise<z.infer<typeof CallToolResultSchema>> { // Corrected response type const validatedArgs = ListLanguagesInputSchema.parse(args); // Validate input const params: { type?: 'source' | 'target' } = {}; if (validatedArgs.type) { params.type = validatedArgs.type; } const response = await this.axiosInstance.get<DeepLLanguage[]>( '/v2/languages', { params } ); const content: TextContent = { type: 'text', text: JSON.stringify(response.data, null, 2), // Pretty print JSON mimeType: 'application/json', }; return { content: [content] }; } // --- Centralized Error Handling for Tools --- private handleToolError(error: unknown): z.infer<typeof CallToolResultSchema> { // Corrected response type let mcpError: McpError; if (error instanceof McpError) { mcpError = error; // Use existing McpError } else if (error instanceof z.ZodError) { // Handle Zod validation errors mcpError = new McpError(ErrorCode.InvalidParams, "Input validation failed", error.errors); } else if (axios.isAxiosError(error)) { // Handle Axios errors specifically const axiosError = error as AxiosError<any>; const status = axiosError.response?.status; const data = axiosError.response?.data; const message = data?.message || axiosError.message; let errorCode = ErrorCode.InternalError; // Default if (status === 400) errorCode = ErrorCode.InvalidParams; // Use available code // Map other client errors (like auth) to InvalidRequest for now if (status && status >= 401 && status < 500 && status !== 400) { errorCode = ErrorCode.InvalidRequest; } // Map server errors (5xx) to InternalError (as UpstreamError isn't available) if (status && status >= 500) { errorCode = ErrorCode.InternalError; // Using InternalError as UpstreamError is not available } mcpError = new McpError(errorCode, `DeepL API Error (${status}): ${message}`, data); } else { // Handle other unexpected errors console.error('Unexpected error calling tool:', error); const errorMessage = error instanceof Error ? error.message : String(error); mcpError = new McpError(ErrorCode.InternalError, `An unexpected error occurred: ${errorMessage}`); } // Format error for MCP response const errorContent: TextContent = { type: 'text', text: `Error: ${mcpError.message}${mcpError.data ? `\nDetails: ${JSON.stringify(mcpError.data)}` : ''}`, }; return { content: [errorContent], isError: true, errorCode: mcpError.code }; } // --- Server Start Method --- async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('DeepL MCP Server started and listening via stdio.'); // Log to stderr } } // --- Initialize and Run --- const server = new DeepLServer(); server.run().catch(error => { console.error("Failed to start DeepL MCP Server:", error); process.exit(1); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/watchdealer-pavel/deepl-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server