#
tokens: 5243/50000 5/5 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .gitignore
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── src
│   └── index.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # Dependencies
 2 | node_modules/
 3 | 
 4 | # Build artifacts
 5 | build/
 6 | 
 7 | # Logs
 8 | *.log
 9 | npm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 | lerna-debug.log*
13 | 
14 | # Diagnostic reports (https://nodejs.org/api/report.html)
15 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
16 | 
17 | # Runtime data
18 | pids
19 | *.pid
20 | *.seed
21 | *.pid.lock
22 | 
23 | # Directory for instrumented libs generated by jscoverage/JSCover
24 | lib-cov
25 | 
26 | # Coverage directory used by tools like istanbul
27 | coverage
28 | *.lcov
29 | 
30 | # nyc test coverage
31 | .nyc_output
32 | 
33 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
34 | .grunt
35 | 
36 | # Bower dependency directory (https://bower.io/)
37 | bower_components
38 | 
39 | # node-waf configuration
40 | .lock-wscript
41 | 
42 | # Compiled binary addons (https://nodejs.org/api/addons.html)
43 | build/Release
44 | 
45 | # Dependency directories
46 | jspm_packages/
47 | 
48 | # TypeScript cache
49 | *.tsbuildinfo
50 | 
51 | # Optional eslint cache
52 | .eslintcache
53 | 
54 | # Output of 'npm pack'
55 | *.tgz
56 | 
57 | # Yarn Integrity file
58 | .yarn-integrity
59 | 
60 | # dotenv environment variables file
61 | .env
62 | .env.test
63 | 
64 | # parcel-bundler cache (https://parceljs.org/)
65 | .cache
66 | 
67 | # Next.js build output
68 | .next
69 | out
70 | 
71 | # Nuxt.js build / generate output
72 | .nuxt
73 | dist
74 | 
75 | # Gatsby files
76 | .cache/
77 | # Comment in the public line in if your project uses Gatsby and not Next.js
78 | # https://nextjs.org/blog/next-9-1#public-directory-support
79 | # public
80 | 
81 | # vuepress build output
82 | .vuepress/dist
83 | 
84 | # Serverless directories
85 | .serverless/
86 | 
87 | # FuseBox cache
88 | .fusebox/
89 | 
90 | # DynamoDB Local files
91 | *.db
92 | *.rdb
93 | 
94 | # TernJS port file
95 | .tern-port
96 | 
97 | # Stores VSCode versions used for testing VSCode extensions
98 | .vscode-test
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # DeepL MCP Server
  2 | 
  3 | An MCP (Model Context Protocol) server providing DeepL translation capabilities.
  4 | 
  5 | ## Features
  6 | 
  7 | This server exposes the following tools via MCP:
  8 | 
  9 | *   **`translate_text`**: Translates one or more text strings between supported languages using the DeepL API.
 10 | *   **`list_languages`**: Retrieves the list of languages supported by the DeepL API (either source or target languages).
 11 | 
 12 | ## Prerequisites
 13 | 
 14 | *   **Node.js and npm/yarn:** Required to install dependencies and run the server.
 15 | *   **DeepL API Key:** You need an API key from DeepL. Both Free and Pro plans provide API access. Sign up or learn more at [https://www.deepl.com/pro-api](https://www.deepl.com/pro-api).
 16 | 
 17 | ## Installation
 18 | 
 19 | 1.  **Clone the repository:**
 20 |     ```bash
 21 |     git clone https://github.com/watchdealer-pavel/deepl-mcp-server.git
 22 |     cd deepl-mcp-server
 23 |     ```
 24 | 
 25 | 2.  **Install dependencies:**
 26 |     ```bash
 27 |     npm install
 28 |     # or
 29 |     # yarn install
 30 |     ```
 31 | 
 32 | 3.  **Build the server:**
 33 |     ```bash
 34 |     npm run build
 35 |     ```
 36 |     This command compiles the TypeScript source code into JavaScript, placing the output in the `build/` directory (specifically `build/index.js`).
 37 | 
 38 | ## Configuration
 39 | 
 40 | The server requires your DeepL API key to be provided via the `DEEPL_API_KEY` environment variable. You need to configure your MCP client (like Cline/Roo Code or the Claude Desktop App) to run this server and pass the environment variable.
 41 | 
 42 | **Example Configuration:**
 43 | 
 44 | Below are examples for common MCP clients. **Remember to replace `/path/to/your/deepl-mcp-server/build/index.js` with the actual absolute path to the compiled server file on your system, and `YOUR_DEEPL_API_KEY` with your real DeepL API key.**
 45 | 
 46 | ### Cline / Roo Code (VS Code Extension)
 47 | 
 48 | 1.  Open your VS Code settings for MCP servers. On macOS, this is typically located at:
 49 |     `~/Library/Application Support/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/mcp_settings.json`
 50 |     *(Note: The exact path might vary based on your operating system and VS Code installation type (e.g., Insiders).)*
 51 | 
 52 | 2.  Add the following configuration block under the `mcpServers` key:
 53 | 
 54 |     ```json
 55 |     "deepl-translator": {
 56 |       "command": "node",
 57 |       "args": ["/path/to/your/deepl-mcp-server/build/index.js"], // <-- IMPORTANT: Replace with the ACTUAL absolute path to build/index.js
 58 |       "env": {
 59 |         "DEEPL_API_KEY": "YOUR_DEEPL_API_KEY" // <-- IMPORTANT: Replace with your DeepL API Key
 60 |       },
 61 |       "disabled": false,
 62 |       "alwaysAllow": []
 63 |     }
 64 |     ```
 65 | 
 66 | ### Claude Desktop App
 67 | 
 68 | 1.  Open the Claude Desktop App configuration file. On macOS, this is typically located at:
 69 |     `~/Library/Application Support/Claude/claude_desktop_config.json`
 70 |     *(Note: The exact path might vary based on your operating system.)*
 71 | 
 72 | 2.  Add the following configuration block under the `mcpServers` key:
 73 | 
 74 |     ```json
 75 |     "deepl-translator": {
 76 |       "command": "node",
 77 |       "args": ["/path/to/your/deepl-mcp-server/build/index.js"], // <-- IMPORTANT: Replace with the ACTUAL absolute path to build/index.js
 78 |       "env": {
 79 |         "DEEPL_API_KEY": "YOUR_DEEPL_API_KEY" // <-- IMPORTANT: Replace with your DeepL API Key
 80 |       },
 81 |       "disabled": false,
 82 |       "alwaysAllow": []
 83 |     }
 84 |     ```
 85 | 
 86 | ## Usage
 87 | 
 88 | Once configured, you can invoke the server's tools from your AI assistant using the `use_mcp_tool` command/tool.
 89 | 
 90 | ### `list_languages` Example
 91 | 
 92 | ```xml
 93 | <use_mcp_tool>
 94 |   <server_name>deepl-translator</server_name>
 95 |   <tool_name>list_languages</tool_name>
 96 |   <arguments>
 97 |     {
 98 |       "type": "target" // Optional: "source" or "target". Defaults to listing all if omitted.
 99 |     }
100 |   </arguments>
101 | </use_mcp_tool>
102 | ```
103 | 
104 | ### `translate_text` Example
105 | 
106 | ```xml
107 | <use_mcp_tool>
108 |   <server_name>deepl-translator</server_name>
109 |   <tool_name>translate_text</tool_name>
110 |   <arguments>
111 |     {
112 |       "text": ["Hello world", "How are you?"], // Required: An array of strings to translate
113 |       "target_lang": "DE", // Required: Target language code (e.g., DE, FR, ES)
114 |       "source_lang": "EN" // Optional: Source language code. DeepL will auto-detect if omitted.
115 |     }
116 |   </arguments>
117 | </use_mcp_tool>
118 | ```
119 | 
120 | ## License
121 | 
122 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
123 | 
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2022",
 4 |     "module": "Node16",
 5 |     "moduleResolution": "Node16",
 6 |     "outDir": "./build",
 7 |     "rootDir": "./src",
 8 |     "strict": true,
 9 |     "esModuleInterop": true,
10 |     "skipLibCheck": true,
11 |     "forceConsistentCasingInFileNames": true
12 |   },
13 |   "include": ["src/**/*"],
14 |   "exclude": ["node_modules"]
15 | }
16 | 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "deepl-mcp-server",
 3 |   "version": "0.1.0",
 4 |   "description": "This server implements the Model Context Protocol (MCP) to provide high-quality text translation services by acting as an interface to the DeepL API.",
 5 |   "private": true,
 6 |   "type": "module",
 7 |   "bin": {
 8 |     "deepl-mcp-server": "./build/index.js"
 9 |   },
10 |   "files": [
11 |     "build"
12 |   ],
13 |   "scripts": {
14 |     "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
15 |     "prepare": "npm run build",
16 |     "watch": "tsc --watch",
17 |     "inspector": "npx @modelcontextprotocol/inspector build/index.js"
18 |   },
19 |   "dependencies": {
20 |     "@modelcontextprotocol/sdk": "^0.6.0",
21 |     "axios": "^1.8.4",
22 |     "zod": "^3.24.2"
23 |   },
24 |   "devDependencies": {
25 |     "@types/node": "^20.11.24",
26 |     "typescript": "^5.3.3"
27 |   }
28 | }
29 | 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | #!/usr/bin/env node
  2 | import { Server } from '@modelcontextprotocol/sdk/server/index.js';
  3 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
  4 | import {
  5 |   CallToolRequestSchema,
  6 |   ListToolsRequestSchema,
  7 |   McpError,
  8 |   ErrorCode,
  9 |   Tool, // Corrected type name
 10 |   CallToolResultSchema, // Corrected type name based on error suggestion
 11 |   ListToolsResultSchema, // Corrected type name based on error suggestion
 12 |   TextContent,
 13 | } from '@modelcontextprotocol/sdk/types.js';
 14 | import axios, { AxiosInstance, AxiosError } from 'axios';
 15 | import { z } from 'zod';
 16 | 
 17 | // --- Environment Variable Check ---
 18 | const API_KEY = process.env.DEEPL_API_KEY;
 19 | if (!API_KEY) {
 20 |   console.error('DEEPL_API_KEY environment variable is not set.');
 21 |   process.exit(1); // Exit if API key is missing
 22 | }
 23 | 
 24 | // --- Input Schemas (using Zod) ---
 25 | const TranslateTextInputSchema = z.object({
 26 |   text: z.array(z.string()).min(1, "The 'text' array cannot be empty."),
 27 |   target_lang: z.string(),
 28 |   source_lang: z.string().optional(),
 29 | });
 30 | 
 31 | const ListLanguagesInputSchema = z.object({
 32 |   type: z.enum(['source', 'target']).optional(),
 33 | });
 34 | 
 35 | // --- DeepL API Response Types ---
 36 | interface DeepLTranslation {
 37 |   detected_source_language: string;
 38 |   text: string;
 39 | }
 40 | 
 41 | interface DeepLLanguage {
 42 |   language: string;
 43 |   name: string;
 44 |   supports_formality: boolean;
 45 | }
 46 | 
 47 | // --- DeepL Server Class ---
 48 | class DeepLServer {
 49 |   private server: Server;
 50 |   private axiosInstance: AxiosInstance;
 51 |   private readonly baseUrl = 'https://api-free.deepl.com';
 52 | 
 53 |   constructor() {
 54 |     this.server = new Server(
 55 |       {
 56 |         name: 'deepl-mcp-server',
 57 |         version: '0.1.0',
 58 |       },
 59 |       {
 60 |         capabilities: {
 61 |           // Resources not implemented in this version
 62 |           resources: {},
 63 |           tools: {}, // Tool handlers are registered below
 64 |         },
 65 |       }
 66 |     );
 67 | 
 68 |     this.axiosInstance = axios.create({
 69 |       baseURL: this.baseUrl,
 70 |       headers: {
 71 |         Authorization: `DeepL-Auth-Key ${API_KEY}`,
 72 |         'Content-Type': 'application/json',
 73 |       },
 74 |     });
 75 | 
 76 |     this.setupToolHandlers();
 77 | 
 78 |     // Basic error logging for the server itself
 79 |     this.server.onerror = (error) => console.error('[MCP Server Error]', error);
 80 |     process.on('SIGINT', async () => {
 81 |         console.error('Received SIGINT, shutting down server...');
 82 |         await this.server.close();
 83 |         process.exit(0);
 84 |       });
 85 |   }
 86 | 
 87 |   private setupToolHandlers() {
 88 |     // --- ListTools Handler ---
 89 |     this.server.setRequestHandler(
 90 |       ListToolsRequestSchema,
 91 |       async (): Promise<z.infer<typeof ListToolsResultSchema>> => { // Corrected response type
 92 |         const tools: Tool[] = [ // Corrected type name
 93 |           {
 94 |             name: 'translate_text',
 95 |             description: 'Translates one or more text strings using the DeepL API.',
 96 |             inputSchema: {
 97 |               type: 'object',
 98 |               properties: {
 99 |                 text: {
100 |                   type: 'array',
101 |                   items: { type: 'string' },
102 |                   description: 'Text(s) to translate',
103 |                 },
104 |                 target_lang: {
105 |                   type: 'string',
106 |                   description: 'Target language code (e.g., DE, FR)',
107 |                 },
108 |                 source_lang: {
109 |                   type: 'string',
110 |                   description: 'Source language code; auto-detected if omitted',
111 |                 },
112 |               },
113 |               required: ['text', 'target_lang'],
114 |             },
115 |           },
116 |           {
117 |             name: 'list_languages',
118 |             description: 'Retrieves the list of languages supported by the DeepL API.',
119 |             inputSchema: {
120 |               type: 'object',
121 |               properties: {
122 |                 type: {
123 |                   type: 'string',
124 |                   enum: ['source', 'target'],
125 |                   description: "Filter by 'source' or 'target' languages",
126 |                 },
127 |               },
128 |               required: [],
129 |             },
130 |           },
131 |         ];
132 |         return { tools };
133 |       }
134 |     );
135 | 
136 |     // --- CallTool Handler ---
137 |     this.server.setRequestHandler(
138 |       CallToolRequestSchema,
139 |       async (request): Promise<z.infer<typeof CallToolResultSchema>> => { // Corrected response type
140 |         try {
141 |           switch (request.params.name) {
142 |             case 'translate_text':
143 |               return await this.callTranslateText(request.params.arguments);
144 |             case 'list_languages':
145 |               return await this.callListLanguages(request.params.arguments);
146 |             default:
147 |               throw new McpError(ErrorCode.MethodNotFound, `Tool '${request.params.name}' not found.`);
148 |           }
149 |         } catch (error) {
150 |             return this.handleToolError(error);
151 |         }
152 |       }
153 |     );
154 |   }
155 | 
156 |   // --- Specific Tool Implementations ---
157 |   private async callTranslateText(args: any): Promise<z.infer<typeof CallToolResultSchema>> { // Corrected response type
158 |     const validatedArgs = TranslateTextInputSchema.parse(args); // Validate input (throws ZodError on failure)
159 | 
160 |     const response = await this.axiosInstance.post<{ translations: DeepLTranslation[] }>(
161 |       '/v2/translate',
162 |       {
163 |         text: validatedArgs.text,
164 |         target_lang: validatedArgs.target_lang,
165 |         ...(validatedArgs.source_lang && { source_lang: validatedArgs.source_lang }),
166 |       }
167 |     );
168 | 
169 |     const content: TextContent = {
170 |         type: 'text',
171 |         text: JSON.stringify(response.data.translations, null, 2), // Pretty print JSON
172 |         mimeType: 'application/json',
173 |     };
174 | 
175 |     return { content: [content] };
176 |   }
177 | 
178 |   private async callListLanguages(args: any): Promise<z.infer<typeof CallToolResultSchema>> { // Corrected response type
179 |     const validatedArgs = ListLanguagesInputSchema.parse(args); // Validate input
180 | 
181 |     const params: { type?: 'source' | 'target' } = {};
182 |     if (validatedArgs.type) {
183 |       params.type = validatedArgs.type;
184 |     }
185 | 
186 |     const response = await this.axiosInstance.get<DeepLLanguage[]>(
187 |       '/v2/languages',
188 |       { params }
189 |     );
190 | 
191 |     const content: TextContent = {
192 |         type: 'text',
193 |         text: JSON.stringify(response.data, null, 2), // Pretty print JSON
194 |         mimeType: 'application/json',
195 |     };
196 | 
197 |     return { content: [content] };
198 |   }
199 | 
200 |   // --- Centralized Error Handling for Tools ---
201 |   private handleToolError(error: unknown): z.infer<typeof CallToolResultSchema> { // Corrected response type
202 |     let mcpError: McpError;
203 | 
204 |     if (error instanceof McpError) {
205 |       mcpError = error; // Use existing McpError
206 |     } else if (error instanceof z.ZodError) {
207 |       // Handle Zod validation errors
208 |       mcpError = new McpError(ErrorCode.InvalidParams, "Input validation failed", error.errors);
209 |     } else if (axios.isAxiosError(error)) {
210 |       // Handle Axios errors specifically
211 |       const axiosError = error as AxiosError<any>;
212 |       const status = axiosError.response?.status;
213 |       const data = axiosError.response?.data;
214 |       const message = data?.message || axiosError.message;
215 | 
216 |       let errorCode = ErrorCode.InternalError; // Default
217 |       if (status === 400) errorCode = ErrorCode.InvalidParams; // Use available code
218 |       // Map other client errors (like auth) to InvalidRequest for now
219 |       if (status && status >= 401 && status < 500 && status !== 400) {
220 |           errorCode = ErrorCode.InvalidRequest;
221 |       }
222 |       // Map server errors (5xx) to InternalError (as UpstreamError isn't available)
223 |       if (status && status >= 500) {
224 |           errorCode = ErrorCode.InternalError; // Using InternalError as UpstreamError is not available
225 |       }
226 | 
227 |       mcpError = new McpError(errorCode, `DeepL API Error (${status}): ${message}`, data);
228 |     } else {
229 |       // Handle other unexpected errors
230 |       console.error('Unexpected error calling tool:', error);
231 |       const errorMessage = error instanceof Error ? error.message : String(error);
232 |       mcpError = new McpError(ErrorCode.InternalError, `An unexpected error occurred: ${errorMessage}`);
233 |     }
234 | 
235 |     // Format error for MCP response
236 |     const errorContent: TextContent = {
237 |         type: 'text',
238 |         text: `Error: ${mcpError.message}${mcpError.data ? `\nDetails: ${JSON.stringify(mcpError.data)}` : ''}`,
239 |     };
240 |     return { content: [errorContent], isError: true, errorCode: mcpError.code };
241 |   }
242 | 
243 | 
244 |   // --- Server Start Method ---
245 |   async run() {
246 |     const transport = new StdioServerTransport();
247 |     await this.server.connect(transport);
248 |     console.error('DeepL MCP Server started and listening via stdio.'); // Log to stderr
249 |   }
250 | }
251 | 
252 | // --- Initialize and Run ---
253 | const server = new DeepLServer();
254 | server.run().catch(error => {
255 |     console.error("Failed to start DeepL MCP Server:", error);
256 |     process.exit(1);
257 | });
258 | 
```