├── .env.example ├── .github ├── logo.png └── social-preview.png ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── assets ├── apple-touch-icon.png ├── deepseek-dark-mode.png ├── favicon-16x16.png ├── favicon-32x32.png └── favicon.ico ├── index.ts ├── package-lock.json ├── package.json ├── smithery.yaml ├── src ├── docs │ ├── deepseek-api-example.md │ └── llms-full.txt ├── index.ts └── types.ts ├── tsconfig.json └── util.ts /.env.example: -------------------------------------------------------------------------------- 1 | # DeepSeek API Configuration 2 | DEEPSEEK_API_KEY=your-api-key-here 3 | -------------------------------------------------------------------------------- /.github/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DMontgomery40/deepseek-mcp-server/1cf59710f952985251b4ce3d49b25e6ec6718f4c/.github/logo.png -------------------------------------------------------------------------------- /.github/social-preview.png: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1. Go to GitHub repository settings 4 | 2. Click on "Social preview" under the repository name 5 | 3. Click "Edit" 6 | 4. Upload the assets/favicon-32x32.png file 7 | 5. GitHub will help resize and position it appropriately 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | .env 4 | *.log 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile 2 | FROM node:18-alpine AS builder 3 | 4 | WORKDIR /app 5 | 6 | # Copy the necessary files 7 | COPY package.json package-lock.json ./ 8 | COPY tsconfig.json ./ 9 | COPY src ./src 10 | 11 | # Install dependencies and build the project 12 | RUN npm install && npm run build 13 | 14 | # Use a smaller base image for the release 15 | FROM node:18-alpine AS release 16 | 17 | WORKDIR /app 18 | 19 | # Copy only the built output and necessary files 20 | COPY --from=builder /app/build ./build 21 | COPY --from=builder /app/package.json ./package.json 22 | COPY --from=builder /app/package-lock.json ./package-lock.json 23 | 24 | # Install production dependencies only 25 | RUN npm install --production 26 | 27 | # Set environment variable 28 | ENV NODE_ENV=production 29 | 30 | # Entry point 31 | ENTRYPOINT ["node", "build/index.js"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 David Montgomery 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DeepSeek MCP Server 2 | 3 | A Model Context Protocol (MCP) server for the DeepSeek API, allowing seamless integration of DeepSeek's powerful language models with MCP-compatible applications like Claude Desktop. 4 | 5 | ## *Anonymously* use DeepSeek API -- Only a proxy is seen on the other side 6 | 7 | DeepSeek Server MCP server 8 | 9 | 10 | [![npm version](https://img.shields.io/npm/v/deepseek-mcp-server)](https://www.npmjs.com/package/deepseek-mcp-server) 11 | [![npm downloads](https://img.shields.io/npm/dm/deepseek-mcp-server)](https://www.npmjs.com/package/deepseek-mcp-server) 12 | [![GitHub issues](https://img.shields.io/github/issues/DMontgomery40/deepseek-mcp-server)](https://github.com/DMontgomery40/deepseek-mcp-server/issues) 13 | [![GitHub forks](https://img.shields.io/github/forks/DMontgomery40/deepseek-mcp-server)](https://github.com/DMontgomery40/deepseek-mcp-server/network) 14 | [![GitHub stars](https://img.shields.io/github/stars/DMontgomery40/deepseek-mcp-server)](https://github.com/DMontgomery40/deepseek-mcp-server/stargazers) 15 | [![GitHub license](https://img.shields.io/github/license/DMontgomery40/deepseek-mcp-server?color=blue)](https://github.com/DMontgomery40/deepseek-mcp-server/blob/main/LICENSE) 16 | 17 | ## Installation 18 | 19 | ### Installing via Smithery 20 | 21 | To install DeepSeek MCP Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@dmontgomery40/deepseek-mcp-server): 22 | 23 | ```bash 24 | npx -y @smithery/cli install @dmontgomery40/deepseek-mcp-server --client claude 25 | ``` 26 | 27 | ### Manual Installation 28 | ```bash 29 | npm install -g deepseek-mcp-server 30 | ``` 31 | ### Usage with Claude Desktop 32 | 33 | Add this to your `claude_desktop_config.json`: 34 | 35 | ```json 36 | { 37 | "mcpServers": { 38 | "deepseek": { 39 | "command": "npx", 40 | "args": [ 41 | "-y", 42 | "deepseek-mcp-server" 43 | ], 44 | "env": { 45 | "DEEPSEEK_API_KEY": "your-api-key" 46 | } 47 | } 48 | } 49 | } 50 | ``` 51 | 52 | ## Features 53 | 54 | > Note: The server intelligently handles these natural language requests by mapping them to appropriate configuration changes. You can also query the current settings and available models: 55 | 56 | - User: "What models are available?" 57 | - Response: Shows list of available models and their capabilities via the models resource. 58 | - User: "What configuration options do I have?" 59 | - Response: Lists all available configuration options via the model-config resource. 60 | - User: "What is the current temperature setting?" 61 | - Response: Displays the current temperature setting. 62 | - User: "Start a multi-turn conversation. With the following settings: model: 'deepseek-chat', make it not too creative, and 63 | allow 8000 tokens." 64 | - Response: *Starts a multi-turn conversation with the specified settings.* 65 | 66 | ### Automatic Model Fallback if R1 is down 67 | 68 | - If the primary model (R1) is down (called `deepseek-reasoner` in the server), the server will automatically attempt to try with v3 (called `deepseek-chat` in the server) 69 | > Note: You can switch back and forth anytime as well, by just giving your prompt and saying "use `deepseek-reasoner`" or "use `deepseek-chat`" 70 | - V3 is recommended for general purpose use, while R1 is recommended for more technical and complex queries, primarily due to speed and token usage 71 | 72 | ### Resource discovery for available models and configurations: 73 | * Custom model selection 74 | * Temperature control (0.0 - 2.0) 75 | * Max tokens limit 76 | * Top P sampling (0.0 - 1.0) 77 | * Presence penalty (-2.0 - 2.0) 78 | * Frequency penalty (-2.0 - 2.0) 79 | 80 | ## Enhanced Conversation Features 81 | 82 | **Multi-turn conversation support:** 83 | * Maintains complete message history and context across exchanges 84 | * Preserves configuration settings throughout the conversation 85 | * Handles complex dialogue flows and follow-up chains automatically 86 | 87 | This feature is particularly valuable for two key use cases: 88 | 89 | 1. **Training & Fine-tuning:** 90 | Since DeepSeek is open source, many users are training their own versions. The multi-turn support provides properly formatted conversation data that's essential for training high-quality dialogue models. 91 | 92 | 2. **Complex Interactions:** 93 | For production use, this helps manage longer conversations where context is crucial: 94 | * Multi-step reasoning problems 95 | * Interactive troubleshooting sessions 96 | * Detailed technical discussions 97 | * Any scenario where context from earlier messages impacts later responses 98 | 99 | The implementation handles all context management and message formatting behind the scenes, letting you focus on the actual interaction rather than the technical details of maintaining conversation state. 100 | 101 | ## Fill-in-the-Middle (FIM) Completion Tool 102 | 103 | The DeepSeek MCP Server now supports the FIM (Fill-in-the-Middle) completion endpoint, which is especially useful for code completion, refactoring, and filling in missing code blocks. 104 | 105 | **Limitations:** 106 | - The FIM endpoint has a 4,096 token (4k) context window. If your prefix + suffix + completion exceeds this, the request will be truncated or may fail. 107 | - FIM is best suited for code and structured text, not general conversation. 108 | 109 | ### How to Use 110 | 111 | You can invoke the FIM tool via MCP clients that support tools, or programmatically. The tool is named `fim_completion` and accepts the following parameters: 112 | 113 | - `prefix` (string, required): The text before the missing section. 114 | - `suffix` (string, required): The text after the missing section. 115 | - `model` (string, optional, default: `deepseek-fim`): The FIM model to use. 116 | - `temperature` (number, optional, default: 0.7): Sampling temperature. 117 | - `max_tokens` (number, optional, default: 1024): Maximum tokens to generate for the middle section. 118 | - `top_p` (number, optional, default: 1.0): Top-p sampling parameter. 119 | - `frequency_penalty` (number, optional, default: 0.1): Frequency penalty. 120 | - `presence_penalty` (number, optional, default: 0): Presence penalty. 121 | - `stream` (boolean, optional, default: false): Whether to stream the response. 122 | 123 | #### Example Usage 124 | 125 | ```json 126 | { 127 | "tool": "fim_completion", 128 | "params": { 129 | "prefix": "def add(a, b):\n return a + b\n\ndef multiply(a, b):\n ", 130 | "suffix": "\n return result\n", 131 | "temperature": 0.2, 132 | "max_tokens": 64 133 | } 134 | } 135 | ``` 136 | 137 | The response will contain the generated code or text that fits between the prefix and suffix. 138 | 139 | **When to use FIM:** 140 | - Completing code blocks where the middle is missing 141 | - Refactoring code by providing the start and end, and letting the model fill in the logic 142 | - Generating boilerplate or repetitive code patterns 143 | - Any scenario where you want the model to generate content that fits between two known sections 144 | 145 | ## Testing with MCP Inspector 146 | 147 | You can test the server locally using the MCP Inspector tool: 148 | 149 | 1. Build the server: 150 | ```bash 151 | npm run build 152 | ``` 153 | 154 | 2. Run the server with MCP Inspector: 155 | ```bash 156 | # Make sure to specify the full path to the built server 157 | npx @modelcontextprotocol/inspector node ./build/index.js 158 | ``` 159 | 160 | The inspector will open in your browser and connect to the server via stdio transport. You can: 161 | - View available tools 162 | - Test chat completions with different parameters 163 | - Debug server responses 164 | - Monitor server performance 165 | 166 | Note: The server uses DeepSeek's R1 model (deepseek-reasoner) by default, which provides state-of-the-art performance for reasoning and general tasks. 167 | 168 | ## License 169 | 170 | MIT 171 | -------------------------------------------------------------------------------- /assets/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DMontgomery40/deepseek-mcp-server/1cf59710f952985251b4ce3d49b25e6ec6718f4c/assets/apple-touch-icon.png -------------------------------------------------------------------------------- /assets/deepseek-dark-mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DMontgomery40/deepseek-mcp-server/1cf59710f952985251b4ce3d49b25e6ec6718f4c/assets/deepseek-dark-mode.png -------------------------------------------------------------------------------- /assets/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DMontgomery40/deepseek-mcp-server/1cf59710f952985251b4ce3d49b25e6ec6718f4c/assets/favicon-16x16.png -------------------------------------------------------------------------------- /assets/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DMontgomery40/deepseek-mcp-server/1cf59710f952985251b4ce3d49b25e6ec6718f4c/assets/favicon-32x32.png -------------------------------------------------------------------------------- /assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DMontgomery40/deepseek-mcp-server/1cf59710f952985251b4ce3d49b25e6ec6718f4c/assets/favicon.ico -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'; 3 | import { ServerCapabilities, ErrorCode, McpError, ListPromptsRequestSchema, GetPromptRequestSchema } from '@modelcontextprotocol/sdk/types.js'; 4 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; 5 | import { array, boolean, optional, string, z, literal } from 'zod'; 6 | import axios, { AxiosError } from 'axios'; 7 | import * as dotenv from 'dotenv'; 8 | import { ConfigSchema, ModelConfig, interpretConfigInstruction } from './util.js'; 9 | import type { DeepSeekResponse, ModelsResponse } from './types.js'; 10 | 11 | // Load environment variables 12 | dotenv.config(); 13 | const DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY; 14 | if (!DEEPSEEK_API_KEY) throw new McpError(ErrorCode.InvalidRequest, 'Missing DEEPSEEK_API_KEY'); 15 | 16 | // Configure server capabilities 17 | const capabilities: ServerCapabilities = { 18 | resources: { enabled: true }, 19 | tools: { enabled: true }, 20 | prompts: { 21 | enabled: true, 22 | templates: ['system', 'chat'], 23 | defaultTemplate: 'chat' 24 | } 25 | }; 26 | 27 | 28 | const transport = new StdioServerTransport(); 29 | await server.connect(transport); 30 | console.log('Connected to server'); 31 | 32 | // Define available prompts 33 | const PROMPTS = { 34 | "system": { 35 | name: "system", 36 | description: "Set system behavior for the AI", 37 | arguments: [ 38 | { 39 | name: "content", 40 | description: "System instruction", 41 | required: true 42 | }, 43 | { 44 | name: "temperature", 45 | description: "Response temperature (0-1)", 46 | required: false 47 | } 48 | ] 49 | }, 50 | "chat": { 51 | name: "chat", 52 | description: "Chat with the AI", 53 | arguments: [ 54 | { 55 | name: "content", 56 | description: "User message", 57 | required: true 58 | }, 59 | { 60 | name: "history", 61 | description: "Previous conversation messages", 62 | required: false 63 | }, 64 | { 65 | name: "temperature", 66 | description: "Response temperature (0-1)", 67 | required: false 68 | }, 69 | { 70 | name: "shouldSummarize", 71 | description: "Whether to summarize the conversation", 72 | required: false 73 | } 74 | ] 75 | } 76 | }; 77 | 78 | // Create Server 79 | const server = new McpServer({ 80 | name: 'deepseek-mcp-server', 81 | version: '1.1.0', 82 | capabilities 83 | }); 84 | 85 | // List available prompts handler 86 | server.setRequestHandler(ListPromptsRequestSchema, async () => { 87 | return { 88 | prompts: Object.values(PROMPTS) 89 | }; 90 | }); 91 | 92 | // Get specific prompt handler 93 | server.setRequestHandler(GetPromptRequestSchema, async (request) => { 94 | const prompt = PROMPTS[request.params.name]; 95 | if (!prompt) { 96 | throw new McpError(ErrorCode.InvalidRequest, `Prompt not found: ${request.params.name}`); 97 | } 98 | 99 | const args = request.params.arguments || {}; 100 | 101 | if (request.params.name === "system") { 102 | if (!args.content) { 103 | throw new McpError(ErrorCode.InvalidRequest, "System prompt requires content argument"); 104 | } 105 | 106 | return { 107 | messages: [ 108 | { 109 | role: 'system', 110 | content: { 111 | type: 'text', 112 | text: args.content 113 | } 114 | } 115 | ], 116 | temperature: args.temperature 117 | }; 118 | } 119 | 120 | if (request.params.name === "chat") { 121 | if (!args.content) { 122 | throw new McpError(ErrorCode.InvalidRequest, "Chat prompt requires content argument"); 123 | } 124 | 125 | const messages = [ 126 | ...(args.history || []), 127 | { 128 | role: 'user', 129 | content: { 130 | type: 'text', 131 | text: args.content 132 | } 133 | } 134 | ]; 135 | 136 | if (args.shouldSummarize) { 137 | messages.push({ 138 | role: 'user', 139 | content: { 140 | type: 'text', 141 | text: 'Please summarize our conversation into key points needed for continuing the discussion.' 142 | } 143 | }); 144 | } 145 | 146 | return { 147 | messages, 148 | temperature: args.temperature ?? modelConfig.temperature 149 | }; 150 | } 151 | 152 | throw new McpError(ErrorCode.InternalError, "Prompt implementation not found"); 153 | }); 154 | 155 | 156 | const client = new McpClient({ 157 | awwa: { 158 | command: 'npx', 159 | args: ['-y', '@modelcontextprotocol/client-awwa', 'http://localhost:3000'] 160 | } 161 | }); 162 | 163 | 164 | // State Management 165 | let modelConfig: ModelConfig = ConfigSchema.parse({}); 166 | 167 | // Define Message Schema 168 | const MessageSchema = z.object({ 169 | role: z.enum(['system', 'user', 'assistant']), 170 | content: z.object({ 171 | type: z.literal('text'), 172 | text: z.string() 173 | }) 174 | }); 175 | 176 | // 1. Register RESOURCES first (they provide data) 177 | server.resource( 178 | 'model-configuration', 179 | new ResourceTemplate('model-config://main', { 180 | list: undefined 181 | }), 182 | async (uri: URL) => ({ 183 | contents: [{ 184 | uri: uri.href, 185 | text: JSON.stringify({ 186 | type: 'model-config', 187 | data: modelConfig 188 | }) 189 | }] 190 | }) 191 | ); 192 | 193 | server.resource( 194 | 'models', 195 | 'models://list', 196 | async (uri: URL) => { 197 | try { 198 | const response = await axios.get( 199 | 'https://api.deepseek.com/models', 200 | { 201 | headers: { 202 | 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`, 203 | 'Content-Type': 'application/json' 204 | } 205 | } 206 | ); 207 | 208 | return { 209 | contents: [{ 210 | uri: uri.href, 211 | text: JSON.stringify(response.data, null, 2) 212 | }] 213 | }; 214 | } catch (error) { 215 | const axiosError = error as AxiosError; 216 | if (axiosError.response) { 217 | throw new McpError( 218 | ErrorCode.InvalidRequest, 219 | `API error: ${JSON.stringify(axiosError.response.data)}` 220 | ); 221 | } 222 | throw new McpError(ErrorCode.InternalError, 'Failed to fetch models list'); 223 | } 224 | } 225 | ); 226 | 227 | // 2. Register TOOLS second (they perform actions) 228 | server.tool( 229 | 'chat-completion', 230 | { 231 | messages: z.array(MessageSchema), 232 | temperature: z.number().optional(), 233 | max_tokens: z.number().optional(), 234 | stop: z.array(z.string()).optional() 235 | }, 236 | async ({ messages, temperature, max_tokens, stop }) => { 237 | try { 238 | const isReasoner = modelConfig.model?.includes('reasoner'); 239 | const payload: Record = { 240 | messages, 241 | model: modelConfig.model || 'deepseek-reasoner', 242 | max_tokens: max_tokens ?? modelConfig.max_tokens, 243 | stop 244 | }; 245 | 246 | if (!isReasoner) { 247 | payload.temperature = temperature ?? modelConfig.temperature; 248 | } 249 | 250 | const response = await axios.post( 251 | 'https://api.deepseek.com/v1/chat/completions', 252 | payload, 253 | { 254 | headers: { 255 | 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`, 256 | 'Content-Type': 'application/json' 257 | } 258 | } 259 | ); 260 | 261 | const result = response.data.choices[0].message; 262 | return { 263 | content: [{ 264 | type: 'text', 265 | text: result.content, 266 | ...(result.reasoning_content && { metadata: { reasoning: result.reasoning_content } }) 267 | }] 268 | }; 269 | } catch (error) { 270 | const axiosError = error as AxiosError; 271 | if (axiosError.response) { 272 | throw new McpError( 273 | ErrorCode.InvalidRequest, 274 | `DeepSeek API error: ${axiosError.response.status} ${axiosError.response.statusText}` 275 | ); 276 | } 277 | throw error; 278 | } 279 | } 280 | ); 281 | 282 | server.tool( 283 | 'configure', 284 | { instruction: z.string() }, 285 | async ({ instruction }: { instruction: string }) => { 286 | try { 287 | const newConfig = await interpretConfigInstruction(instruction); 288 | modelConfig = ConfigSchema.parse(newConfig); 289 | return { 290 | content: [{ 291 | type: 'text', 292 | text: `Configuration updated:\n${JSON.stringify(modelConfig, null, 2)}` 293 | }] 294 | }; 295 | } catch (error) { 296 | return { 297 | content: [{ 298 | type: 'text', 299 | text: `Configuration error: ${error instanceof Error ? error.message : String(error)}` 300 | }], 301 | error: new McpError(ErrorCode.InvalidRequest, 'Invalid configuration instruction') 302 | }; 303 | } 304 | } 305 | ); 306 | 307 | server.tool( 308 | 'multi-round-chat', 309 | { 310 | messages: z.array(MessageSchema), 311 | temperature: z.number().optional(), 312 | shouldSummarize: z.boolean().optional() 313 | }, 314 | async ({ messages, temperature, shouldSummarize = false }) => { 315 | try { 316 | const isReasoner = modelConfig.model?.includes('reasoner'); 317 | 318 | if (shouldSummarize) { 319 | const summaryResponse = await axios.post( 320 | 'https://api.deepseek.com/v1/chat/completions', 321 | { 322 | messages: [...messages, { 323 | role: 'user', 324 | content: { 325 | type: 'text', 326 | text: 'Please summarize our conversation into key points needed for continuing the discussion.' 327 | } 328 | }], 329 | model: modelConfig.model, 330 | temperature: 0.3, 331 | max_tokens: modelConfig.max_tokens 332 | }, 333 | { 334 | headers: { 335 | 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`, 336 | 'Content-Type': 'application/json' 337 | } 338 | } 339 | ); 340 | 341 | const summary = summaryResponse.data.choices[0].message; 342 | return { 343 | content: [{ 344 | type: 'text', 345 | text: summary.content, 346 | ...(summary.reasoning_content && { metadata: { reasoning: summary.reasoning_content } }) 347 | }] 348 | }; 349 | } 350 | 351 | const payload: Record = { 352 | messages, 353 | model: modelConfig.model, 354 | max_tokens: modelConfig.max_tokens 355 | }; 356 | 357 | if (!isReasoner) { 358 | payload.temperature = temperature ?? modelConfig.temperature; 359 | } 360 | 361 | const response = await axios.post( 362 | 'https://api.deepseek.com/v1/chat/completions', 363 | payload, 364 | { 365 | headers: { 366 | 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`, 367 | 'Content-Type': 'application/json' 368 | } 369 | } 370 | ); 371 | 372 | const result = response.data.choices[0].message; 373 | return { 374 | content: [{ 375 | type: 'text', 376 | text: result.content, 377 | ...(result.reasoning_content && { metadata: { reasoning: result.reasoning_content } }) 378 | }] 379 | }; 380 | } catch (error) { 381 | const axiosError = error as AxiosError; 382 | if (axiosError.response) { 383 | throw new McpError( 384 | ErrorCode.InvalidRequest, 385 | `API error: ${JSON.stringify(axiosError.response.data)}` 386 | ); 387 | } 388 | throw new McpError(ErrorCode.InternalError, 'Failed to process chat request'); 389 | } 390 | } 391 | ); 392 | 393 | server.tool( 394 | 'continue-conversation', 395 | { 396 | previousMessages: z.array(MessageSchema), 397 | newQuestion: z.string(), 398 | shouldSummarize: z.boolean().optional() 399 | }, 400 | async ({ previousMessages, newQuestion, shouldSummarize = false }) => { 401 | try { 402 | if (shouldSummarize) { 403 | const summaryResponse = await axios.post( 404 | 'https://api.deepseek.com/v1/chat/completions', 405 | { 406 | messages: [ 407 | ...previousMessages, 408 | { 409 | role: 'user', 410 | content: { 411 | type: 'text', 412 | text: 'Please summarize our conversation so far into key points.' 413 | } 414 | } 415 | ], 416 | model: modelConfig.model, 417 | temperature: 0.3 418 | }, 419 | { 420 | headers: { 421 | 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`, 422 | 'Content-Type': 'application/json' 423 | } 424 | } 425 | ); 426 | 427 | const summary = summaryResponse.data.choices[0].message; 428 | return { 429 | content: [{ 430 | type: 'text', 431 | text: `Previous conversation summary: ${summary.content}` 432 | }] 433 | }; 434 | } 435 | 436 | const response = await axios.post( 437 | 'https://api.deepseek.com/v1/chat/completions', 438 | { 439 | messages: [...previousMessages, { role: 'user', content: { type: 'text', text: newQuestion } }], 440 | model: modelConfig.model, 441 | temperature: modelConfig.temperature 442 | }, 443 | { 444 | headers: { 445 | 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`, 446 | 'Content-Type': 'application/json' 447 | } 448 | } 449 | ); 450 | 451 | const result = response.data.choices[0].message; 452 | return { 453 | content: [{ 454 | type: 'text', 455 | text: result.content 456 | }] 457 | }; 458 | } catch (error) { 459 | const axiosError = error as AxiosError; 460 | if (axiosError.response) { 461 | throw new McpError( 462 | ErrorCode.InvalidRequest, 463 | `API error: ${JSON.stringify(axiosError.response.data)}` 464 | ); 465 | } 466 | throw new McpError(ErrorCode.InternalError, 'Failed to process conversation'); 467 | } 468 | } 469 | ); 470 | 471 | // 3. Register PROMPTS last (they're templates for LLM interaction) 472 | server.prompt( 473 | 'system', // Handler for system prompts 474 | { 475 | content: z.string(), 476 | temperature: z.number().optional(), 477 | stop: z.array(z.string()).optional() 478 | }, 479 | async ({ content, temperature, stop }) => { 480 | const messages = [{ 481 | role: 'system', 482 | content: { type: 'text', text: content } 483 | }]; 484 | 485 | return { messages, stop }; 486 | } 487 | ); 488 | 489 | server.prompt( 490 | 'chat', // Handler for chat interactions 491 | { 492 | content: z.string(), 493 | history: z.array(MessageSchema).optional(), 494 | temperature: z.number().optional(), 495 | shouldSummarize: z.boolean().optional(), 496 | stop: z.array(z.string()).optional() 497 | }, 498 | async ({ content, history = [], temperature, shouldSummarize = false, stop }) => { 499 | try { 500 | const messages = [ 501 | ...history, 502 | { 503 | role: 'user', 504 | content: { type: 'text', text: content } 505 | } 506 | ]; 507 | 508 | if (shouldSummarize) { 509 | messages.push({ 510 | role: 'user', 511 | content: { 512 | type: 'text', 513 | text: 'Please summarize our conversation into key points needed for continuing the discussion.' 514 | } 515 | }); 516 | } 517 | 518 | return { 519 | messages, 520 | temperature: temperature ?? modelConfig.temperature, 521 | stop 522 | }; 523 | } catch (error) { 524 | throw new McpError( 525 | ErrorCode.InternalError, 526 | `Failed to process chat prompt: ${error instanceof Error ? error.message : String(error)}` 527 | ); 528 | } 529 | } 530 | ); 531 | 532 | server.prompt( 533 | 'chat', // Handler for chat interactions 534 | { 535 | content: z.string(), 536 | history: z.array(MessageSchema).optional(), 537 | temperature: z.number().optional(), 538 | shouldSummarize: z.boolean().optional(), 539 | stop: z.array(z.string()).optional() 540 | }, 541 | async ({ type, content, messages = [], temperature, shouldSummarize = false, stopSequences }) => { 542 | try { 543 | const isReasoner = modelConfig.model?.includes('reasoner'); 544 | 545 | // Handle system prompts 546 | if (type === 'system') { 547 | return { 548 | messages: [{ 549 | role: 'system', 550 | content // Content is already in the right format 551 | }], 552 | stop: stopSequences 553 | }; 554 | } 555 | 556 | // Handle chat interactions 557 | if (shouldSummarize) { 558 | const summaryResponse = await axios.post( 559 | 'https://api.deepseek.com/v1/chat/completions', 560 | { 561 | messages: [...messages, { 562 | role: 'user', 563 | content: { 564 | type: 'text', 565 | text: 'Please summarize our conversation into key points needed for continuing the discussion.' 566 | } 567 | }], 568 | model: modelConfig.model, 569 | temperature: 0.3, 570 | max_tokens: modelConfig.max_tokens 571 | }, 572 | { 573 | headers: { 574 | 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`, 575 | 'Content-Type': 'application/json' 576 | } 577 | } 578 | ); 579 | 580 | const summary = summaryResponse.data.choices[0].message; 581 | return { 582 | content: [{ 583 | type: 'text', 584 | text: summary.content, 585 | ...(summary.reasoning_content && { metadata: { reasoning: summary.reasoning_content } }) 586 | }] 587 | }; 588 | } 589 | 590 | const payload: Record = { 591 | messages: [...messages, { role: 'user', content }], // Content is already in the right format 592 | model: modelConfig.model, 593 | max_tokens: modelConfig.max_tokens 594 | }; 595 | 596 | if (!isReasoner) { 597 | payload.temperature = temperature ?? modelConfig.temperature; 598 | } 599 | 600 | const response = await axios.post( 601 | 'https://api.deepseek.com/v1/chat/completions', 602 | payload, 603 | { 604 | headers: { 605 | 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`, 606 | 'Content-Type': 'application/json' 607 | } 608 | } 609 | ); 610 | 611 | const result = response.data.choices[0].message; 612 | return { 613 | content: [{ 614 | type: 'text', 615 | text: result.content, 616 | ...(result.reasoning_content && { metadata: { reasoning: result.reasoning_content } }) 617 | }] 618 | }; 619 | } catch (error) { 620 | const axiosError = error as AxiosError; 621 | if (axiosError.response) { 622 | throw new McpError( 623 | ErrorCode.InvalidRequest, 624 | `API error: ${JSON.stringify(axiosError.response.data)}` 625 | ); 626 | } 627 | throw new McpError(ErrorCode.InternalError, 'Failed to process request'); 628 | } 629 | } 630 | ); 631 | 632 | // Configure Transport 633 | const transport = new StdioServerTransport(); 634 | 635 | // Error Handling 636 | process.on('uncaughtException', (error: Error) => { 637 | process.stderr.write(`Uncaught exception: ${error.stack ?? error.message}\n`); 638 | process.exit(1); 639 | }); 640 | 641 | process.on('unhandledRejection', (reason: unknown) => { 642 | process.stderr.write(`Unhandled rejection: ${reason instanceof Error ? reason.stack : String(reason)}\n`); 643 | process.exit(1); 644 | }); 645 | 646 | // Initialize prompt templates 647 | const systemTemplate = { 648 | type: 'system', 649 | content: { 650 | type: 'text' as const, 651 | text: '' 652 | } 653 | }; 654 | 655 | const chatTemplate = { 656 | type: 'chat', 657 | content: { 658 | type: 'text' as const, 659 | text: '' 660 | }, 661 | messages: [] 662 | }; 663 | 664 | // Set up prompt templates before connecting 665 | server.setPromptTemplates({ 666 | system: systemTemplate, 667 | chat: chatTemplate 668 | }); 669 | 670 | // Connect server 671 | server.connect(transport).catch((error: Error) => { 672 | process.stderr.write(`Transport error: ${error.stack ?? error.message}\n`); 673 | process.exit(1); 674 | }); -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "deepseek-mcp-server", 3 | "version": "0.2.1", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "deepseek-mcp-server", 9 | "version": "0.2.1", 10 | "license": "MIT", 11 | "dependencies": { 12 | "@modelcontextprotocol/sdk": "^1.12.1", 13 | "axios": "^1.6.0", 14 | "dotenv": "^16.3.1" 15 | }, 16 | "bin": { 17 | "deepseek-mcp-server": "build/index.js" 18 | }, 19 | "devDependencies": { 20 | "@types/node": "^20.19.0", 21 | "typescript": "^5.2.2" 22 | } 23 | }, 24 | "node_modules/@modelcontextprotocol/sdk": { 25 | "version": "1.12.1", 26 | "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.12.1.tgz", 27 | "integrity": "sha512-KG1CZhZfWg+u8pxeM/mByJDScJSrjjxLc8fwQqbsS8xCjBmQfMNEBTotYdNanKekepnfRI85GtgQlctLFpcYPw==", 28 | "license": "MIT", 29 | "dependencies": { 30 | "ajv": "^6.12.6", 31 | "content-type": "^1.0.5", 32 | "cors": "^2.8.5", 33 | "cross-spawn": "^7.0.5", 34 | "eventsource": "^3.0.2", 35 | "express": "^5.0.1", 36 | "express-rate-limit": "^7.5.0", 37 | "pkce-challenge": "^5.0.0", 38 | "raw-body": "^3.0.0", 39 | "zod": "^3.23.8", 40 | "zod-to-json-schema": "^3.24.1" 41 | }, 42 | "engines": { 43 | "node": ">=18" 44 | } 45 | }, 46 | "node_modules/@types/node": { 47 | "version": "20.19.0", 48 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.0.tgz", 49 | "integrity": "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==", 50 | "dev": true, 51 | "license": "MIT", 52 | "dependencies": { 53 | "undici-types": "~6.21.0" 54 | } 55 | }, 56 | "node_modules/accepts": { 57 | "version": "2.0.0", 58 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", 59 | "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", 60 | "license": "MIT", 61 | "dependencies": { 62 | "mime-types": "^3.0.0", 63 | "negotiator": "^1.0.0" 64 | }, 65 | "engines": { 66 | "node": ">= 0.6" 67 | } 68 | }, 69 | "node_modules/ajv": { 70 | "version": "6.12.6", 71 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 72 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 73 | "license": "MIT", 74 | "dependencies": { 75 | "fast-deep-equal": "^3.1.1", 76 | "fast-json-stable-stringify": "^2.0.0", 77 | "json-schema-traverse": "^0.4.1", 78 | "uri-js": "^4.2.2" 79 | }, 80 | "funding": { 81 | "type": "github", 82 | "url": "https://github.com/sponsors/epoberezkin" 83 | } 84 | }, 85 | "node_modules/asynckit": { 86 | "version": "0.4.0", 87 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 88 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", 89 | "license": "MIT" 90 | }, 91 | "node_modules/axios": { 92 | "version": "1.9.0", 93 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", 94 | "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", 95 | "license": "MIT", 96 | "dependencies": { 97 | "follow-redirects": "^1.15.6", 98 | "form-data": "^4.0.0", 99 | "proxy-from-env": "^1.1.0" 100 | } 101 | }, 102 | "node_modules/body-parser": { 103 | "version": "2.2.0", 104 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", 105 | "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", 106 | "license": "MIT", 107 | "dependencies": { 108 | "bytes": "^3.1.2", 109 | "content-type": "^1.0.5", 110 | "debug": "^4.4.0", 111 | "http-errors": "^2.0.0", 112 | "iconv-lite": "^0.6.3", 113 | "on-finished": "^2.4.1", 114 | "qs": "^6.14.0", 115 | "raw-body": "^3.0.0", 116 | "type-is": "^2.0.0" 117 | }, 118 | "engines": { 119 | "node": ">=18" 120 | } 121 | }, 122 | "node_modules/bytes": { 123 | "version": "3.1.2", 124 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 125 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 126 | "license": "MIT", 127 | "engines": { 128 | "node": ">= 0.8" 129 | } 130 | }, 131 | "node_modules/call-bind-apply-helpers": { 132 | "version": "1.0.2", 133 | "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", 134 | "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", 135 | "license": "MIT", 136 | "dependencies": { 137 | "es-errors": "^1.3.0", 138 | "function-bind": "^1.1.2" 139 | }, 140 | "engines": { 141 | "node": ">= 0.4" 142 | } 143 | }, 144 | "node_modules/call-bound": { 145 | "version": "1.0.4", 146 | "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", 147 | "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", 148 | "license": "MIT", 149 | "dependencies": { 150 | "call-bind-apply-helpers": "^1.0.2", 151 | "get-intrinsic": "^1.3.0" 152 | }, 153 | "engines": { 154 | "node": ">= 0.4" 155 | }, 156 | "funding": { 157 | "url": "https://github.com/sponsors/ljharb" 158 | } 159 | }, 160 | "node_modules/combined-stream": { 161 | "version": "1.0.8", 162 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 163 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 164 | "license": "MIT", 165 | "dependencies": { 166 | "delayed-stream": "~1.0.0" 167 | }, 168 | "engines": { 169 | "node": ">= 0.8" 170 | } 171 | }, 172 | "node_modules/content-disposition": { 173 | "version": "1.0.0", 174 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", 175 | "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", 176 | "license": "MIT", 177 | "dependencies": { 178 | "safe-buffer": "5.2.1" 179 | }, 180 | "engines": { 181 | "node": ">= 0.6" 182 | } 183 | }, 184 | "node_modules/content-type": { 185 | "version": "1.0.5", 186 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 187 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", 188 | "license": "MIT", 189 | "engines": { 190 | "node": ">= 0.6" 191 | } 192 | }, 193 | "node_modules/cookie": { 194 | "version": "0.7.2", 195 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", 196 | "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", 197 | "license": "MIT", 198 | "engines": { 199 | "node": ">= 0.6" 200 | } 201 | }, 202 | "node_modules/cookie-signature": { 203 | "version": "1.2.2", 204 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", 205 | "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", 206 | "license": "MIT", 207 | "engines": { 208 | "node": ">=6.6.0" 209 | } 210 | }, 211 | "node_modules/cors": { 212 | "version": "2.8.5", 213 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 214 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 215 | "license": "MIT", 216 | "dependencies": { 217 | "object-assign": "^4", 218 | "vary": "^1" 219 | }, 220 | "engines": { 221 | "node": ">= 0.10" 222 | } 223 | }, 224 | "node_modules/cross-spawn": { 225 | "version": "7.0.6", 226 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", 227 | "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", 228 | "license": "MIT", 229 | "dependencies": { 230 | "path-key": "^3.1.0", 231 | "shebang-command": "^2.0.0", 232 | "which": "^2.0.1" 233 | }, 234 | "engines": { 235 | "node": ">= 8" 236 | } 237 | }, 238 | "node_modules/debug": { 239 | "version": "4.4.1", 240 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", 241 | "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", 242 | "license": "MIT", 243 | "dependencies": { 244 | "ms": "^2.1.3" 245 | }, 246 | "engines": { 247 | "node": ">=6.0" 248 | }, 249 | "peerDependenciesMeta": { 250 | "supports-color": { 251 | "optional": true 252 | } 253 | } 254 | }, 255 | "node_modules/delayed-stream": { 256 | "version": "1.0.0", 257 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 258 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 259 | "license": "MIT", 260 | "engines": { 261 | "node": ">=0.4.0" 262 | } 263 | }, 264 | "node_modules/depd": { 265 | "version": "2.0.0", 266 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 267 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 268 | "license": "MIT", 269 | "engines": { 270 | "node": ">= 0.8" 271 | } 272 | }, 273 | "node_modules/dotenv": { 274 | "version": "16.5.0", 275 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", 276 | "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", 277 | "license": "BSD-2-Clause", 278 | "engines": { 279 | "node": ">=12" 280 | }, 281 | "funding": { 282 | "url": "https://dotenvx.com" 283 | } 284 | }, 285 | "node_modules/dunder-proto": { 286 | "version": "1.0.1", 287 | "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", 288 | "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", 289 | "license": "MIT", 290 | "dependencies": { 291 | "call-bind-apply-helpers": "^1.0.1", 292 | "es-errors": "^1.3.0", 293 | "gopd": "^1.2.0" 294 | }, 295 | "engines": { 296 | "node": ">= 0.4" 297 | } 298 | }, 299 | "node_modules/ee-first": { 300 | "version": "1.1.1", 301 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 302 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", 303 | "license": "MIT" 304 | }, 305 | "node_modules/encodeurl": { 306 | "version": "2.0.0", 307 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", 308 | "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", 309 | "license": "MIT", 310 | "engines": { 311 | "node": ">= 0.8" 312 | } 313 | }, 314 | "node_modules/es-define-property": { 315 | "version": "1.0.1", 316 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", 317 | "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", 318 | "license": "MIT", 319 | "engines": { 320 | "node": ">= 0.4" 321 | } 322 | }, 323 | "node_modules/es-errors": { 324 | "version": "1.3.0", 325 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 326 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 327 | "license": "MIT", 328 | "engines": { 329 | "node": ">= 0.4" 330 | } 331 | }, 332 | "node_modules/es-object-atoms": { 333 | "version": "1.1.1", 334 | "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", 335 | "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", 336 | "license": "MIT", 337 | "dependencies": { 338 | "es-errors": "^1.3.0" 339 | }, 340 | "engines": { 341 | "node": ">= 0.4" 342 | } 343 | }, 344 | "node_modules/es-set-tostringtag": { 345 | "version": "2.1.0", 346 | "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", 347 | "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", 348 | "license": "MIT", 349 | "dependencies": { 350 | "es-errors": "^1.3.0", 351 | "get-intrinsic": "^1.2.6", 352 | "has-tostringtag": "^1.0.2", 353 | "hasown": "^2.0.2" 354 | }, 355 | "engines": { 356 | "node": ">= 0.4" 357 | } 358 | }, 359 | "node_modules/escape-html": { 360 | "version": "1.0.3", 361 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 362 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", 363 | "license": "MIT" 364 | }, 365 | "node_modules/etag": { 366 | "version": "1.8.1", 367 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 368 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", 369 | "license": "MIT", 370 | "engines": { 371 | "node": ">= 0.6" 372 | } 373 | }, 374 | "node_modules/eventsource": { 375 | "version": "3.0.7", 376 | "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", 377 | "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", 378 | "license": "MIT", 379 | "dependencies": { 380 | "eventsource-parser": "^3.0.1" 381 | }, 382 | "engines": { 383 | "node": ">=18.0.0" 384 | } 385 | }, 386 | "node_modules/eventsource-parser": { 387 | "version": "3.0.2", 388 | "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.2.tgz", 389 | "integrity": "sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA==", 390 | "license": "MIT", 391 | "engines": { 392 | "node": ">=18.0.0" 393 | } 394 | }, 395 | "node_modules/express": { 396 | "version": "5.1.0", 397 | "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", 398 | "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", 399 | "license": "MIT", 400 | "dependencies": { 401 | "accepts": "^2.0.0", 402 | "body-parser": "^2.2.0", 403 | "content-disposition": "^1.0.0", 404 | "content-type": "^1.0.5", 405 | "cookie": "^0.7.1", 406 | "cookie-signature": "^1.2.1", 407 | "debug": "^4.4.0", 408 | "encodeurl": "^2.0.0", 409 | "escape-html": "^1.0.3", 410 | "etag": "^1.8.1", 411 | "finalhandler": "^2.1.0", 412 | "fresh": "^2.0.0", 413 | "http-errors": "^2.0.0", 414 | "merge-descriptors": "^2.0.0", 415 | "mime-types": "^3.0.0", 416 | "on-finished": "^2.4.1", 417 | "once": "^1.4.0", 418 | "parseurl": "^1.3.3", 419 | "proxy-addr": "^2.0.7", 420 | "qs": "^6.14.0", 421 | "range-parser": "^1.2.1", 422 | "router": "^2.2.0", 423 | "send": "^1.1.0", 424 | "serve-static": "^2.2.0", 425 | "statuses": "^2.0.1", 426 | "type-is": "^2.0.1", 427 | "vary": "^1.1.2" 428 | }, 429 | "engines": { 430 | "node": ">= 18" 431 | }, 432 | "funding": { 433 | "type": "opencollective", 434 | "url": "https://opencollective.com/express" 435 | } 436 | }, 437 | "node_modules/express-rate-limit": { 438 | "version": "7.5.0", 439 | "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz", 440 | "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==", 441 | "license": "MIT", 442 | "engines": { 443 | "node": ">= 16" 444 | }, 445 | "funding": { 446 | "url": "https://github.com/sponsors/express-rate-limit" 447 | }, 448 | "peerDependencies": { 449 | "express": "^4.11 || 5 || ^5.0.0-beta.1" 450 | } 451 | }, 452 | "node_modules/fast-deep-equal": { 453 | "version": "3.1.3", 454 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 455 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 456 | "license": "MIT" 457 | }, 458 | "node_modules/fast-json-stable-stringify": { 459 | "version": "2.1.0", 460 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 461 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 462 | "license": "MIT" 463 | }, 464 | "node_modules/finalhandler": { 465 | "version": "2.1.0", 466 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", 467 | "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", 468 | "license": "MIT", 469 | "dependencies": { 470 | "debug": "^4.4.0", 471 | "encodeurl": "^2.0.0", 472 | "escape-html": "^1.0.3", 473 | "on-finished": "^2.4.1", 474 | "parseurl": "^1.3.3", 475 | "statuses": "^2.0.1" 476 | }, 477 | "engines": { 478 | "node": ">= 0.8" 479 | } 480 | }, 481 | "node_modules/follow-redirects": { 482 | "version": "1.15.9", 483 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", 484 | "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", 485 | "funding": [ 486 | { 487 | "type": "individual", 488 | "url": "https://github.com/sponsors/RubenVerborgh" 489 | } 490 | ], 491 | "license": "MIT", 492 | "engines": { 493 | "node": ">=4.0" 494 | }, 495 | "peerDependenciesMeta": { 496 | "debug": { 497 | "optional": true 498 | } 499 | } 500 | }, 501 | "node_modules/form-data": { 502 | "version": "4.0.3", 503 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", 504 | "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", 505 | "license": "MIT", 506 | "dependencies": { 507 | "asynckit": "^0.4.0", 508 | "combined-stream": "^1.0.8", 509 | "es-set-tostringtag": "^2.1.0", 510 | "hasown": "^2.0.2", 511 | "mime-types": "^2.1.12" 512 | }, 513 | "engines": { 514 | "node": ">= 6" 515 | } 516 | }, 517 | "node_modules/form-data/node_modules/mime-db": { 518 | "version": "1.52.0", 519 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 520 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 521 | "license": "MIT", 522 | "engines": { 523 | "node": ">= 0.6" 524 | } 525 | }, 526 | "node_modules/form-data/node_modules/mime-types": { 527 | "version": "2.1.35", 528 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 529 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 530 | "license": "MIT", 531 | "dependencies": { 532 | "mime-db": "1.52.0" 533 | }, 534 | "engines": { 535 | "node": ">= 0.6" 536 | } 537 | }, 538 | "node_modules/forwarded": { 539 | "version": "0.2.0", 540 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 541 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 542 | "license": "MIT", 543 | "engines": { 544 | "node": ">= 0.6" 545 | } 546 | }, 547 | "node_modules/fresh": { 548 | "version": "2.0.0", 549 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", 550 | "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", 551 | "license": "MIT", 552 | "engines": { 553 | "node": ">= 0.8" 554 | } 555 | }, 556 | "node_modules/function-bind": { 557 | "version": "1.1.2", 558 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 559 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 560 | "license": "MIT", 561 | "funding": { 562 | "url": "https://github.com/sponsors/ljharb" 563 | } 564 | }, 565 | "node_modules/get-intrinsic": { 566 | "version": "1.3.0", 567 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", 568 | "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", 569 | "license": "MIT", 570 | "dependencies": { 571 | "call-bind-apply-helpers": "^1.0.2", 572 | "es-define-property": "^1.0.1", 573 | "es-errors": "^1.3.0", 574 | "es-object-atoms": "^1.1.1", 575 | "function-bind": "^1.1.2", 576 | "get-proto": "^1.0.1", 577 | "gopd": "^1.2.0", 578 | "has-symbols": "^1.1.0", 579 | "hasown": "^2.0.2", 580 | "math-intrinsics": "^1.1.0" 581 | }, 582 | "engines": { 583 | "node": ">= 0.4" 584 | }, 585 | "funding": { 586 | "url": "https://github.com/sponsors/ljharb" 587 | } 588 | }, 589 | "node_modules/get-proto": { 590 | "version": "1.0.1", 591 | "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", 592 | "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", 593 | "license": "MIT", 594 | "dependencies": { 595 | "dunder-proto": "^1.0.1", 596 | "es-object-atoms": "^1.0.0" 597 | }, 598 | "engines": { 599 | "node": ">= 0.4" 600 | } 601 | }, 602 | "node_modules/gopd": { 603 | "version": "1.2.0", 604 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", 605 | "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", 606 | "license": "MIT", 607 | "engines": { 608 | "node": ">= 0.4" 609 | }, 610 | "funding": { 611 | "url": "https://github.com/sponsors/ljharb" 612 | } 613 | }, 614 | "node_modules/has-symbols": { 615 | "version": "1.1.0", 616 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", 617 | "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", 618 | "license": "MIT", 619 | "engines": { 620 | "node": ">= 0.4" 621 | }, 622 | "funding": { 623 | "url": "https://github.com/sponsors/ljharb" 624 | } 625 | }, 626 | "node_modules/has-tostringtag": { 627 | "version": "1.0.2", 628 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", 629 | "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", 630 | "license": "MIT", 631 | "dependencies": { 632 | "has-symbols": "^1.0.3" 633 | }, 634 | "engines": { 635 | "node": ">= 0.4" 636 | }, 637 | "funding": { 638 | "url": "https://github.com/sponsors/ljharb" 639 | } 640 | }, 641 | "node_modules/hasown": { 642 | "version": "2.0.2", 643 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 644 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 645 | "license": "MIT", 646 | "dependencies": { 647 | "function-bind": "^1.1.2" 648 | }, 649 | "engines": { 650 | "node": ">= 0.4" 651 | } 652 | }, 653 | "node_modules/http-errors": { 654 | "version": "2.0.0", 655 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 656 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 657 | "license": "MIT", 658 | "dependencies": { 659 | "depd": "2.0.0", 660 | "inherits": "2.0.4", 661 | "setprototypeof": "1.2.0", 662 | "statuses": "2.0.1", 663 | "toidentifier": "1.0.1" 664 | }, 665 | "engines": { 666 | "node": ">= 0.8" 667 | } 668 | }, 669 | "node_modules/http-errors/node_modules/statuses": { 670 | "version": "2.0.1", 671 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 672 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 673 | "license": "MIT", 674 | "engines": { 675 | "node": ">= 0.8" 676 | } 677 | }, 678 | "node_modules/iconv-lite": { 679 | "version": "0.6.3", 680 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", 681 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", 682 | "license": "MIT", 683 | "dependencies": { 684 | "safer-buffer": ">= 2.1.2 < 3.0.0" 685 | }, 686 | "engines": { 687 | "node": ">=0.10.0" 688 | } 689 | }, 690 | "node_modules/inherits": { 691 | "version": "2.0.4", 692 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 693 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 694 | "license": "ISC" 695 | }, 696 | "node_modules/ipaddr.js": { 697 | "version": "1.9.1", 698 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 699 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 700 | "license": "MIT", 701 | "engines": { 702 | "node": ">= 0.10" 703 | } 704 | }, 705 | "node_modules/is-promise": { 706 | "version": "4.0.0", 707 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", 708 | "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", 709 | "license": "MIT" 710 | }, 711 | "node_modules/isexe": { 712 | "version": "2.0.0", 713 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 714 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 715 | "license": "ISC" 716 | }, 717 | "node_modules/json-schema-traverse": { 718 | "version": "0.4.1", 719 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 720 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 721 | "license": "MIT" 722 | }, 723 | "node_modules/math-intrinsics": { 724 | "version": "1.1.0", 725 | "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", 726 | "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", 727 | "license": "MIT", 728 | "engines": { 729 | "node": ">= 0.4" 730 | } 731 | }, 732 | "node_modules/media-typer": { 733 | "version": "1.1.0", 734 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", 735 | "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", 736 | "license": "MIT", 737 | "engines": { 738 | "node": ">= 0.8" 739 | } 740 | }, 741 | "node_modules/merge-descriptors": { 742 | "version": "2.0.0", 743 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", 744 | "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", 745 | "license": "MIT", 746 | "engines": { 747 | "node": ">=18" 748 | }, 749 | "funding": { 750 | "url": "https://github.com/sponsors/sindresorhus" 751 | } 752 | }, 753 | "node_modules/mime-db": { 754 | "version": "1.54.0", 755 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", 756 | "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", 757 | "license": "MIT", 758 | "engines": { 759 | "node": ">= 0.6" 760 | } 761 | }, 762 | "node_modules/mime-types": { 763 | "version": "3.0.1", 764 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", 765 | "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", 766 | "license": "MIT", 767 | "dependencies": { 768 | "mime-db": "^1.54.0" 769 | }, 770 | "engines": { 771 | "node": ">= 0.6" 772 | } 773 | }, 774 | "node_modules/ms": { 775 | "version": "2.1.3", 776 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 777 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 778 | "license": "MIT" 779 | }, 780 | "node_modules/negotiator": { 781 | "version": "1.0.0", 782 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", 783 | "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", 784 | "license": "MIT", 785 | "engines": { 786 | "node": ">= 0.6" 787 | } 788 | }, 789 | "node_modules/object-assign": { 790 | "version": "4.1.1", 791 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 792 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 793 | "license": "MIT", 794 | "engines": { 795 | "node": ">=0.10.0" 796 | } 797 | }, 798 | "node_modules/object-inspect": { 799 | "version": "1.13.4", 800 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", 801 | "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", 802 | "license": "MIT", 803 | "engines": { 804 | "node": ">= 0.4" 805 | }, 806 | "funding": { 807 | "url": "https://github.com/sponsors/ljharb" 808 | } 809 | }, 810 | "node_modules/on-finished": { 811 | "version": "2.4.1", 812 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 813 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 814 | "license": "MIT", 815 | "dependencies": { 816 | "ee-first": "1.1.1" 817 | }, 818 | "engines": { 819 | "node": ">= 0.8" 820 | } 821 | }, 822 | "node_modules/once": { 823 | "version": "1.4.0", 824 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 825 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 826 | "license": "ISC", 827 | "dependencies": { 828 | "wrappy": "1" 829 | } 830 | }, 831 | "node_modules/parseurl": { 832 | "version": "1.3.3", 833 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 834 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 835 | "license": "MIT", 836 | "engines": { 837 | "node": ">= 0.8" 838 | } 839 | }, 840 | "node_modules/path-key": { 841 | "version": "3.1.1", 842 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 843 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 844 | "license": "MIT", 845 | "engines": { 846 | "node": ">=8" 847 | } 848 | }, 849 | "node_modules/path-to-regexp": { 850 | "version": "8.2.0", 851 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", 852 | "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", 853 | "license": "MIT", 854 | "engines": { 855 | "node": ">=16" 856 | } 857 | }, 858 | "node_modules/pkce-challenge": { 859 | "version": "5.0.0", 860 | "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", 861 | "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", 862 | "license": "MIT", 863 | "engines": { 864 | "node": ">=16.20.0" 865 | } 866 | }, 867 | "node_modules/proxy-addr": { 868 | "version": "2.0.7", 869 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 870 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 871 | "license": "MIT", 872 | "dependencies": { 873 | "forwarded": "0.2.0", 874 | "ipaddr.js": "1.9.1" 875 | }, 876 | "engines": { 877 | "node": ">= 0.10" 878 | } 879 | }, 880 | "node_modules/proxy-from-env": { 881 | "version": "1.1.0", 882 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 883 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", 884 | "license": "MIT" 885 | }, 886 | "node_modules/punycode": { 887 | "version": "2.3.1", 888 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 889 | "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 890 | "license": "MIT", 891 | "engines": { 892 | "node": ">=6" 893 | } 894 | }, 895 | "node_modules/qs": { 896 | "version": "6.14.0", 897 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", 898 | "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", 899 | "license": "BSD-3-Clause", 900 | "dependencies": { 901 | "side-channel": "^1.1.0" 902 | }, 903 | "engines": { 904 | "node": ">=0.6" 905 | }, 906 | "funding": { 907 | "url": "https://github.com/sponsors/ljharb" 908 | } 909 | }, 910 | "node_modules/range-parser": { 911 | "version": "1.2.1", 912 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 913 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 914 | "license": "MIT", 915 | "engines": { 916 | "node": ">= 0.6" 917 | } 918 | }, 919 | "node_modules/raw-body": { 920 | "version": "3.0.0", 921 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", 922 | "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", 923 | "license": "MIT", 924 | "dependencies": { 925 | "bytes": "3.1.2", 926 | "http-errors": "2.0.0", 927 | "iconv-lite": "0.6.3", 928 | "unpipe": "1.0.0" 929 | }, 930 | "engines": { 931 | "node": ">= 0.8" 932 | } 933 | }, 934 | "node_modules/router": { 935 | "version": "2.2.0", 936 | "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", 937 | "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", 938 | "license": "MIT", 939 | "dependencies": { 940 | "debug": "^4.4.0", 941 | "depd": "^2.0.0", 942 | "is-promise": "^4.0.0", 943 | "parseurl": "^1.3.3", 944 | "path-to-regexp": "^8.0.0" 945 | }, 946 | "engines": { 947 | "node": ">= 18" 948 | } 949 | }, 950 | "node_modules/safe-buffer": { 951 | "version": "5.2.1", 952 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 953 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 954 | "funding": [ 955 | { 956 | "type": "github", 957 | "url": "https://github.com/sponsors/feross" 958 | }, 959 | { 960 | "type": "patreon", 961 | "url": "https://www.patreon.com/feross" 962 | }, 963 | { 964 | "type": "consulting", 965 | "url": "https://feross.org/support" 966 | } 967 | ], 968 | "license": "MIT" 969 | }, 970 | "node_modules/safer-buffer": { 971 | "version": "2.1.2", 972 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 973 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 974 | "license": "MIT" 975 | }, 976 | "node_modules/send": { 977 | "version": "1.2.0", 978 | "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", 979 | "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", 980 | "license": "MIT", 981 | "dependencies": { 982 | "debug": "^4.3.5", 983 | "encodeurl": "^2.0.0", 984 | "escape-html": "^1.0.3", 985 | "etag": "^1.8.1", 986 | "fresh": "^2.0.0", 987 | "http-errors": "^2.0.0", 988 | "mime-types": "^3.0.1", 989 | "ms": "^2.1.3", 990 | "on-finished": "^2.4.1", 991 | "range-parser": "^1.2.1", 992 | "statuses": "^2.0.1" 993 | }, 994 | "engines": { 995 | "node": ">= 18" 996 | } 997 | }, 998 | "node_modules/serve-static": { 999 | "version": "2.2.0", 1000 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", 1001 | "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", 1002 | "license": "MIT", 1003 | "dependencies": { 1004 | "encodeurl": "^2.0.0", 1005 | "escape-html": "^1.0.3", 1006 | "parseurl": "^1.3.3", 1007 | "send": "^1.2.0" 1008 | }, 1009 | "engines": { 1010 | "node": ">= 18" 1011 | } 1012 | }, 1013 | "node_modules/setprototypeof": { 1014 | "version": "1.2.0", 1015 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 1016 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", 1017 | "license": "ISC" 1018 | }, 1019 | "node_modules/shebang-command": { 1020 | "version": "2.0.0", 1021 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1022 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1023 | "license": "MIT", 1024 | "dependencies": { 1025 | "shebang-regex": "^3.0.0" 1026 | }, 1027 | "engines": { 1028 | "node": ">=8" 1029 | } 1030 | }, 1031 | "node_modules/shebang-regex": { 1032 | "version": "3.0.0", 1033 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1034 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 1035 | "license": "MIT", 1036 | "engines": { 1037 | "node": ">=8" 1038 | } 1039 | }, 1040 | "node_modules/side-channel": { 1041 | "version": "1.1.0", 1042 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", 1043 | "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", 1044 | "license": "MIT", 1045 | "dependencies": { 1046 | "es-errors": "^1.3.0", 1047 | "object-inspect": "^1.13.3", 1048 | "side-channel-list": "^1.0.0", 1049 | "side-channel-map": "^1.0.1", 1050 | "side-channel-weakmap": "^1.0.2" 1051 | }, 1052 | "engines": { 1053 | "node": ">= 0.4" 1054 | }, 1055 | "funding": { 1056 | "url": "https://github.com/sponsors/ljharb" 1057 | } 1058 | }, 1059 | "node_modules/side-channel-list": { 1060 | "version": "1.0.0", 1061 | "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", 1062 | "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", 1063 | "license": "MIT", 1064 | "dependencies": { 1065 | "es-errors": "^1.3.0", 1066 | "object-inspect": "^1.13.3" 1067 | }, 1068 | "engines": { 1069 | "node": ">= 0.4" 1070 | }, 1071 | "funding": { 1072 | "url": "https://github.com/sponsors/ljharb" 1073 | } 1074 | }, 1075 | "node_modules/side-channel-map": { 1076 | "version": "1.0.1", 1077 | "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", 1078 | "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", 1079 | "license": "MIT", 1080 | "dependencies": { 1081 | "call-bound": "^1.0.2", 1082 | "es-errors": "^1.3.0", 1083 | "get-intrinsic": "^1.2.5", 1084 | "object-inspect": "^1.13.3" 1085 | }, 1086 | "engines": { 1087 | "node": ">= 0.4" 1088 | }, 1089 | "funding": { 1090 | "url": "https://github.com/sponsors/ljharb" 1091 | } 1092 | }, 1093 | "node_modules/side-channel-weakmap": { 1094 | "version": "1.0.2", 1095 | "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", 1096 | "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", 1097 | "license": "MIT", 1098 | "dependencies": { 1099 | "call-bound": "^1.0.2", 1100 | "es-errors": "^1.3.0", 1101 | "get-intrinsic": "^1.2.5", 1102 | "object-inspect": "^1.13.3", 1103 | "side-channel-map": "^1.0.1" 1104 | }, 1105 | "engines": { 1106 | "node": ">= 0.4" 1107 | }, 1108 | "funding": { 1109 | "url": "https://github.com/sponsors/ljharb" 1110 | } 1111 | }, 1112 | "node_modules/statuses": { 1113 | "version": "2.0.2", 1114 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", 1115 | "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", 1116 | "license": "MIT", 1117 | "engines": { 1118 | "node": ">= 0.8" 1119 | } 1120 | }, 1121 | "node_modules/toidentifier": { 1122 | "version": "1.0.1", 1123 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 1124 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 1125 | "license": "MIT", 1126 | "engines": { 1127 | "node": ">=0.6" 1128 | } 1129 | }, 1130 | "node_modules/type-is": { 1131 | "version": "2.0.1", 1132 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", 1133 | "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", 1134 | "license": "MIT", 1135 | "dependencies": { 1136 | "content-type": "^1.0.5", 1137 | "media-typer": "^1.1.0", 1138 | "mime-types": "^3.0.0" 1139 | }, 1140 | "engines": { 1141 | "node": ">= 0.6" 1142 | } 1143 | }, 1144 | "node_modules/typescript": { 1145 | "version": "5.8.3", 1146 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", 1147 | "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", 1148 | "dev": true, 1149 | "license": "Apache-2.0", 1150 | "bin": { 1151 | "tsc": "bin/tsc", 1152 | "tsserver": "bin/tsserver" 1153 | }, 1154 | "engines": { 1155 | "node": ">=14.17" 1156 | } 1157 | }, 1158 | "node_modules/undici-types": { 1159 | "version": "6.21.0", 1160 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", 1161 | "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", 1162 | "dev": true, 1163 | "license": "MIT" 1164 | }, 1165 | "node_modules/unpipe": { 1166 | "version": "1.0.0", 1167 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1168 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", 1169 | "license": "MIT", 1170 | "engines": { 1171 | "node": ">= 0.8" 1172 | } 1173 | }, 1174 | "node_modules/uri-js": { 1175 | "version": "4.4.1", 1176 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 1177 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 1178 | "license": "BSD-2-Clause", 1179 | "dependencies": { 1180 | "punycode": "^2.1.0" 1181 | } 1182 | }, 1183 | "node_modules/vary": { 1184 | "version": "1.1.2", 1185 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1186 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", 1187 | "license": "MIT", 1188 | "engines": { 1189 | "node": ">= 0.8" 1190 | } 1191 | }, 1192 | "node_modules/which": { 1193 | "version": "2.0.2", 1194 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1195 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1196 | "license": "ISC", 1197 | "dependencies": { 1198 | "isexe": "^2.0.0" 1199 | }, 1200 | "bin": { 1201 | "node-which": "bin/node-which" 1202 | }, 1203 | "engines": { 1204 | "node": ">= 8" 1205 | } 1206 | }, 1207 | "node_modules/wrappy": { 1208 | "version": "1.0.2", 1209 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1210 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 1211 | "license": "ISC" 1212 | }, 1213 | "node_modules/zod": { 1214 | "version": "3.25.56", 1215 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.56.tgz", 1216 | "integrity": "sha512-rd6eEF3BTNvQnR2e2wwolfTmUTnp70aUTqr0oaGbHifzC3BKJsoV+Gat8vxUMR1hwOKBs6El+qWehrHbCpW6SQ==", 1217 | "license": "MIT", 1218 | "funding": { 1219 | "url": "https://github.com/sponsors/colinhacks" 1220 | } 1221 | }, 1222 | "node_modules/zod-to-json-schema": { 1223 | "version": "3.24.5", 1224 | "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", 1225 | "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", 1226 | "license": "ISC", 1227 | "peerDependencies": { 1228 | "zod": "^3.24.1" 1229 | } 1230 | } 1231 | } 1232 | } 1233 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "deepseek-mcp-server", 3 | "version": "1.2.0", 4 | "description": "Model Context Protocol server for DeepSeek's advanced language models.", 5 | "type": "module", 6 | "main": "build/index.js", 7 | "bin": { 8 | "deepseek-mcp-server": "./build/index.js" 9 | }, 10 | "files": [ 11 | "build/**/*", 12 | "assets/favicon.ico", 13 | "assets/favicon-32x32.png", 14 | "assets/apple-touch-icon.png" 15 | ], 16 | "scripts": { 17 | "build": "tsc", 18 | "start": "node build/index.js", 19 | "prepublishOnly": "npm run build", 20 | "inspect": "npx @modelcontextprotocol/inspector build/index.js" 21 | }, 22 | "dependencies": { 23 | "@modelcontextprotocol/sdk": "^1.12.1", 24 | "axios": "^1.6.0", 25 | "dotenv": "^16.3.1" 26 | }, 27 | "devDependencies": { 28 | "@types/node": "^20.19.0", 29 | "typescript": "^5.2.2" 30 | }, 31 | "author": "DMontgomery40", 32 | "license": "MIT", 33 | "repository": { 34 | "type": "git", 35 | "url": "https://github.com/DMontgomery40/deepseek-mcp-server" 36 | }, 37 | "keywords": [ 38 | "mcp", 39 | "deepseek", 40 | "ai", 41 | "llm" 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /smithery.yaml: -------------------------------------------------------------------------------- 1 | # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml 2 | 3 | startCommand: 4 | type: stdio 5 | configSchema: 6 | # JSON Schema defining the configuration options for the MCP. 7 | type: object 8 | required: 9 | - deepseekApiKey 10 | properties: 11 | deepseekApiKey: 12 | type: string 13 | description: The API key for the DeepSeek server. 14 | commandFunction: 15 | # A function that produces the CLI command to start the MCP on stdio. 16 | |- 17 | (config) => ({ command: 'node', args: ['build/index.js'], env: { DEEPSEEK_API_KEY: config.deepseekApiKey } }) -------------------------------------------------------------------------------- /src/docs/deepseek-api-example.md: -------------------------------------------------------------------------------- 1 | Reasoning Model (deepseek-reasoner) 2 | deepseek-reasoner is a reasoning model developed by DeepSeek. Before delivering the final answer, the model first generates a Chain of Thought (CoT) to enhance the accuracy of its responses. Our API provides users with access to the CoT content generated by deepseek-reasoner, enabling them to view, display, and distill it. 3 | 4 | When using deepseek-reasoner, please upgrade the OpenAI SDK first to support the new parameters. 5 | 6 | pip3 install -U openai 7 | 8 | API Parameters 9 | Input: 10 | 11 | max_tokens:The maximum length of the final response after the CoT output is completed, defaulting to 4K, with a maximum of 8K. Note that the CoT output can reach up to 32K tokens, and the parameter to control the CoT length (reasoning_effort) will be available soon. 12 | Output: 13 | 14 | reasoning_content:The content of the CoT,which is at the same level as content in the output structure. See API Example for details 15 | contentThe content of the final answer 16 | Context Length:The API supports a maximum context length of 64K, and the length of the output reasoning_content is not counted within the 64K context length. 17 | 18 | Supported Features:Chat Completion、Chat Prefix Completion (Beta) 19 | 20 | Not Supported Features:Function Call、Json Output、FIM (Beta) 21 | 22 | Not Supported Parameters:temperature、top_p、presence_penalty、frequency_penalty、logprobs、top_logprobs. Please note that to ensure compatibility with existing software, setting these parameters will not trigger an error but will also have no effect. 23 | 24 | Multi-round Conversation 25 | In each round of the conversation, the model outputs the CoT (reasoning_content) and the final answer (content). In the next round of the conversation, the CoT from previous rounds is not concatenated into the context, as illustrated in the following diagram: 26 | 27 | 28 | Please note that if the reasoning_content field is included in the sequence of input messages, the API will return a 400 error. Therefore, you should remove the reasoning_content field from the API response before making the API request, as demonstrated in the API example. 29 | 30 | API Example 31 | The following code, using Python as an example, demonstrates how to access the CoT and the final answer, as well as how to conduct multi-round conversations: 32 | 33 | NoStreaming 34 | Streaming 35 | from openai import OpenAI 36 | client = OpenAI(api_key="", base_url="https://api.deepseek.com") 37 | 38 | # Round 1 39 | messages = [{"role": "user", "content": "9.11 and 9.8, which is greater?"}] 40 | response = client.chat.completions.create( 41 | model="deepseek-reasoner", 42 | messages=messages 43 | ) 44 | 45 | reasoning_content = response.choices[0].message.reasoning_content 46 | content = response.choices[0].message.content 47 | 48 | # Round 2 49 | messages.append({'role': 'assistant', 'content': content}) 50 | messages.append({'role': 'user', 'content': "How many Rs are there in the word 'strawberry'?"}) 51 | response = client.chat.completions.create( 52 | model="deepseek-reasoner", 53 | messages=messages 54 | ) 55 | # ... 56 | 57 | 58 | Previous 59 | Get User Balance 60 | Next 61 | Multi-round Conversation 62 | API Parameters 63 | Multi-round Conversation 64 | API Example -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // NOTE: Ensure @modelcontextprotocol/sdk is installed for the following import to work 3 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 4 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; 5 | import { 6 | ListPromptsRequestSchema, 7 | GetPromptRequestSchema, 8 | ErrorCode, 9 | McpError 10 | } from "@modelcontextprotocol/sdk/types.js"; 11 | import axios from "axios"; 12 | import dotenv from "dotenv"; 13 | import { DeepSeekResponse, ChatMessage } from "./types"; 14 | import { z } from "zod"; 15 | 16 | // Load environment variables 17 | dotenv.config(); 18 | const DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY; 19 | if (!DEEPSEEK_API_KEY) throw new McpError(ErrorCode.InvalidRequest, "Missing DEEPSEEK_API_KEY"); 20 | 21 | // Define prompt templates for protocol compliance 22 | const PROMPTS = { 23 | system: { 24 | name: "system", 25 | description: "Set system behavior for the AI", 26 | arguments: [ 27 | { name: "content", description: "System instruction", required: true } 28 | ] 29 | }, 30 | chat: { 31 | name: "chat", 32 | description: "Chat with the AI", 33 | arguments: [ 34 | { name: "message", description: "User message", required: true }, 35 | { name: "temperature", description: "Response temperature (0-1)", required: false } 36 | ] 37 | } 38 | }; 39 | 40 | const server = new McpServer({ 41 | name: "deepseek-mcp-server", 42 | version: "1.1.0" 43 | }); 44 | 45 | // Register prompt templates using server.prompt() 46 | server.prompt( 47 | "system", 48 | { content: z.string() }, 49 | async ({ content }: { content: string }) => { 50 | return { 51 | messages: [ 52 | { 53 | role: "user", 54 | content: { 55 | type: "text", 56 | text: `[System Instruction]\n${content}` 57 | } 58 | } 59 | ] 60 | }; 61 | } 62 | ); 63 | 64 | server.prompt( 65 | "chat", 66 | { message: z.string(), temperature: z.string().optional() }, 67 | async ({ message, temperature }: { message: string; temperature?: string }) => { 68 | return { 69 | messages: [ 70 | { 71 | role: "user", 72 | content: { 73 | type: "text", 74 | text: message 75 | } 76 | } 77 | ], 78 | temperature 79 | }; 80 | } 81 | ); 82 | 83 | // Register prompts/list handler 84 | server.server.setRequestHandler(ListPromptsRequestSchema, async () => ({ 85 | prompts: Object.values(PROMPTS) 86 | })); 87 | 88 | // Register prompts/get handler 89 | server.server.setRequestHandler(GetPromptRequestSchema, async (request: any) => { 90 | const prompt = PROMPTS[request.params.name as keyof typeof PROMPTS]; 91 | if (!prompt) { 92 | throw new McpError(ErrorCode.InvalidRequest, `Prompt not found: ${request.params.name}`); 93 | } 94 | if (request.params.name === "system") { 95 | return { 96 | messages: [{ 97 | role: "user", 98 | content: { 99 | type: "text", 100 | text: `[System Instruction]\n${request.params.arguments?.content ?? ""}` 101 | } 102 | }] 103 | }; 104 | } 105 | if (request.params.name === "chat") { 106 | return { 107 | messages: [{ 108 | role: "user", 109 | content: { 110 | type: "text", 111 | text: request.params.arguments?.message ?? "" 112 | } 113 | }], 114 | temperature: request.params.arguments?.temperature 115 | }; 116 | } 117 | throw new McpError(ErrorCode.InvalidRequest, "Unknown prompt type"); 118 | }); 119 | 120 | // Example: Register a chat-completion tool (update as needed) 121 | server.tool( 122 | "chat_completion", 123 | { 124 | message: { type: "string", optional: true }, 125 | messages: { type: "array", items: { type: "object" }, optional: true }, 126 | model: { type: "string", default: "deepseek-chat" }, 127 | temperature: { type: "number", default: 0.7 }, 128 | max_tokens: { type: "number", default: 8000 }, 129 | top_p: { type: "number", default: 1.0 }, 130 | frequency_penalty: { type: "number", default: 0.1 }, 131 | presence_penalty: { type: "number", default: 0 }, 132 | reasoning_content: { type: "string", optional: true }, 133 | stream: { type: "boolean", default: false } 134 | }, 135 | async (args: any) => { 136 | let messages: ChatMessage[]; 137 | if (args.message) { 138 | messages = [{ role: 'user', content: args.message }]; 139 | } else if (args.messages) { 140 | messages = args.messages; 141 | } else { 142 | throw new McpError(ErrorCode.InvalidRequest, "Either 'message' or 'messages' must be provided"); 143 | } 144 | try { 145 | const response = await axios.post( 146 | "https://api.deepseek.com/chat/completions", 147 | { 148 | messages, 149 | model: args.model, 150 | temperature: args.temperature, 151 | max_tokens: args.max_tokens, 152 | top_p: args.top_p, 153 | frequency_penalty: args.frequency_penalty, 154 | presence_penalty: args.presence_penalty, 155 | reasoning_content: args.reasoning_content, 156 | stream: args.stream 157 | }, 158 | { 159 | headers: { 160 | 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`, 161 | 'Content-Type': 'application/json' 162 | } 163 | } 164 | ); 165 | return { 166 | content: [{ 167 | type: "text", 168 | text: response.data.choices[0].message.content 169 | }] 170 | }; 171 | } catch (error: any) { 172 | throw new McpError(ErrorCode.InternalError, error.message); 173 | } 174 | } 175 | ); 176 | 177 | async function main() { 178 | const transport = new StdioServerTransport(); 179 | await server.connect(transport); 180 | console.error("DeepSeek MCP server running on stdio"); 181 | } 182 | 183 | main().catch(console.error); -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export interface DeepSeekResponse { 2 | id: string; 3 | object: string; 4 | created: number; 5 | model: string; 6 | choices: Array<{ 7 | index: number; 8 | message: { 9 | role: 'assistant'; 10 | content: string; 11 | reasoning_content?: string; 12 | }; 13 | delta?: { 14 | content?: string; 15 | reasoning_content?: string; 16 | }; 17 | finish_reason: 'stop' | 'length' | 'content_filter'; 18 | }>; 19 | usage: { 20 | prompt_tokens: number; 21 | completion_tokens: number; 22 | total_tokens: number; 23 | }; 24 | } 25 | 26 | export interface Prompt { 27 | content: string; 28 | type: 'system' | 'user' | 'assistant'; 29 | reasoning?: boolean; 30 | temperature?: number; 31 | stop_sequences?: string[]; 32 | } 33 | 34 | export interface ChatMessage { 35 | role: 'system' | 'user' | 'assistant'; 36 | content: string; 37 | reasoning_content?: string; 38 | } 39 | 40 | export interface ChatCompletionArgs { 41 | message?: string; 42 | messages?: ChatMessage[]; 43 | model?: string; 44 | temperature?: number; 45 | max_tokens?: number; 46 | top_p?: number; 47 | frequency_penalty?: number; 48 | presence_penalty?: number; 49 | reasoning_content?: string; 50 | stream?: boolean; 51 | } 52 | 53 | // Type guard for chat completion arguments 54 | export interface ModelInfo { 55 | id: string; 56 | name: string; 57 | description: string; 58 | } 59 | 60 | export interface ModelConfig { 61 | id: string; 62 | name: string; 63 | type: 'number' | 'integer' | 'string' | 'boolean'; 64 | description: string; 65 | default?: any; 66 | minimum?: number; 67 | maximum?: number; 68 | } 69 | 70 | export function isValidChatCompletionArgs(args: unknown): args is ChatCompletionArgs { 71 | if (!args || typeof args !== 'object') { 72 | return false; 73 | } 74 | 75 | const { message, messages, model, temperature, max_tokens, top_p, frequency_penalty, presence_penalty, reasoning_content, stream } = args as ChatCompletionArgs; 76 | 77 | // Must have either message or messages 78 | if (!message && !messages) { 79 | return false; 80 | } 81 | 82 | // If message is provided, it must be a string 83 | if (message !== undefined && typeof message !== 'string') { 84 | return false; 85 | } 86 | 87 | // If messages is provided, validate the array 88 | if (messages !== undefined) { 89 | if (!Array.isArray(messages)) { 90 | return false; 91 | } 92 | 93 | const validRoles = ['system', 'user', 'assistant'] as const; 94 | const isValidMessage = (msg: unknown): msg is ChatMessage => { 95 | if (!msg || typeof msg !== 'object') { 96 | return false; 97 | } 98 | const { role, content } = msg as ChatMessage; 99 | return ( 100 | validRoles.includes(role as typeof validRoles[number]) && 101 | typeof content === 'string' 102 | ); 103 | }; 104 | 105 | if (!messages.every(isValidMessage)) { 106 | return false; 107 | } 108 | } 109 | 110 | if (model !== undefined && typeof model !== 'string') { 111 | return false; 112 | } 113 | 114 | if (temperature !== undefined && (typeof temperature !== 'number' || temperature < 0 || temperature > 2)) { 115 | return false; 116 | } 117 | 118 | if (max_tokens !== undefined && (typeof max_tokens !== 'number' || max_tokens < 1)) { 119 | return false; 120 | } 121 | 122 | if (top_p !== undefined && (typeof top_p !== 'number' || top_p < 0 || top_p > 1)) { 123 | return false; 124 | } 125 | 126 | if (frequency_penalty !== undefined && (typeof frequency_penalty !== 'number' || frequency_penalty < -2 || frequency_penalty > 2)) { 127 | return false; 128 | } 129 | 130 | if (presence_penalty !== undefined && (typeof presence_penalty !== 'number' || presence_penalty < -2 || presence_penalty > 2)) { 131 | return false; 132 | } 133 | 134 | if (reasoning_content !== undefined && typeof reasoning_content !== 'string') { 135 | return false; 136 | } 137 | 138 | if (stream !== undefined && typeof stream !== 'boolean') { 139 | return false; 140 | } 141 | 142 | return true; 143 | } 144 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ES2020", 5 | "moduleResolution": "node", 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 | -------------------------------------------------------------------------------- /util.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'; 3 | 4 | export const ConfigSchema = z.object({ 5 | model: z.string() 6 | .default('deepseek-chat') 7 | .describe('Model identifier for DeepSeek API'), 8 | temperature: z.number() 9 | .min(0) 10 | .max(2) 11 | .default(0.7) 12 | .describe('Controls randomness in generation'), 13 | max_tokens: z.number() 14 | .min(1) 15 | .max(8000) 16 | .default(8000) 17 | .describe('Maximum tokens to generate'), 18 | top_p: z.number() 19 | .min(0) 20 | .max(1) 21 | .default(1) 22 | .describe('Nucleus sampling threshold'), 23 | }); 24 | 25 | export type ModelConfig = z.infer; 26 | 27 | export async function interpretConfigInstruction( 28 | instruction: string 29 | ): Promise { 30 | const config: ModelConfig = { 31 | model: 'deepseek-chat', 32 | temperature: 0.7, 33 | max_tokens: 8000, 34 | top_p: 1 35 | }; 36 | 37 | const normalizedInstruction = instruction.toLowerCase(); 38 | 39 | // Model selection 40 | if (normalizedInstruction.includes('reason')) { 41 | config.model = 'deepseek-reasoner'; 42 | } else if (normalizedInstruction.includes('chat')) { 43 | config.model = 'deepseek-chat'; 44 | } 45 | 46 | // Temperature parsing 47 | const tempMatch = normalizedInstruction.match(/temperature[:\s]+(\d*\.?\d+)/); 48 | if (tempMatch) { 49 | const temp = parseFloat(tempMatch[1]); 50 | if (!isNaN(temp) && temp >= 0 && temp <= 2) { 51 | config.temperature = temp; 52 | } else { 53 | throw new McpError( 54 | ErrorCode.InvalidRequest, 55 | 'Temperature must be between 0 and 2' 56 | ); 57 | } 58 | } else if (normalizedInstruction.includes('high') || normalizedInstruction.includes('creative')) { 59 | config.temperature = 0.8; 60 | } else if (normalizedInstruction.includes('low') || normalizedInstruction.includes('precise')) { 61 | config.temperature = 0.2; 62 | } 63 | 64 | // Token length parsing 65 | const tokenMatch = normalizedInstruction.match(/(\d+)\s*(tokens|length)/); 66 | if (tokenMatch) { 67 | const tokens = parseInt(tokenMatch[1]); 68 | if (!isNaN(tokens) && tokens > 0) { 69 | config.max_tokens = Math.min(8000, tokens); 70 | } else { 71 | throw new McpError( 72 | ErrorCode.InvalidRequest, 73 | 'Token length must be a positive number' 74 | ); 75 | } 76 | } 77 | 78 | // Top-p parsing 79 | const topPMatch = normalizedInstruction.match(/top[_\s-]?p[:\s]+(\d*\.?\d+)/); 80 | if (topPMatch) { 81 | const topP = parseFloat(topPMatch[1]); 82 | if (!isNaN(topP) && topP >= 0 && topP <= 1) { 83 | config.top_p = topP; 84 | } else { 85 | throw new McpError( 86 | ErrorCode.InvalidRequest, 87 | 'Top-p must be between 0 and 1' 88 | ); 89 | } 90 | } 91 | 92 | try { 93 | return ConfigSchema.parse(config); 94 | } catch (error) { 95 | throw new McpError( 96 | ErrorCode.InvalidRequest, 97 | 'Invalid configuration parameters generated from instruction' 98 | ); 99 | } 100 | } --------------------------------------------------------------------------------