{"repo": "arikusi/deepseek-mcp-server", "files": {"server.json": {"5eb18b60abee16172cb9d04e70b4c46fd470783a": {"t": 1785674050.6794374, "n": 1366, "h": "992d9fefaa4c6b8b", "c": "{\n \"$schema\": \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\",\n \"name\": \"io.github.arikusi/deepseek\",\n \"description\": \"MCP server for DeepSeek AI with chat, reasoning, sessions, function calling, and cost tracking\",\n \"repository\": {\n \"url\": \"https://github.com/arikusi/deepseek-mcp-server\",\n \"source\": \"github\"\n },\n \"version\": \"2.2.0\",\n \"websiteUrl\": \"https://mcp.tahirl.com\",\n \"icon\": \"https://raw.githubusercontent.com/arikusi/deepseek-mcp-server/main/icon.png\",\n \"packages\": [\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"2.2.0\",\n \"transport\": {\n \"type\": \"stdio\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n },\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"2.2.0\",\n \"transport\": {\n \"type\": \"streamable-http\",\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key (sent as Bearer token)\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n }\n ]\n}\n"}, "361db56a39908561fe8f6b9c2227cbcf8c098d1a": {"t": 1785749504.053672, "n": 1366, "h": "992d9fefaa4c6b8b", "c": "{\n \"$schema\": \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\",\n \"name\": \"io.github.arikusi/deepseek\",\n \"description\": \"MCP server for DeepSeek AI with chat, reasoning, sessions, function calling, and cost tracking\",\n \"repository\": {\n \"url\": \"https://github.com/arikusi/deepseek-mcp-server\",\n \"source\": \"github\"\n },\n \"version\": \"2.2.0\",\n \"websiteUrl\": \"https://mcp.tahirl.com\",\n \"icon\": \"https://raw.githubusercontent.com/arikusi/deepseek-mcp-server/main/icon.png\",\n \"packages\": [\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"2.2.0\",\n \"transport\": {\n \"type\": \"stdio\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n },\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"2.2.0\",\n \"transport\": {\n \"type\": \"streamable-http\",\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key (sent as Bearer token)\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n }\n ]\n}\n"}, "f54b61e09e3baf2233e65c66c830ff43d4a87d46": {"t": 1785749505.1045375, "n": 1366, "h": "be9e43799d1dac87", "c": "{\n \"$schema\": \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\",\n \"name\": \"io.github.arikusi/deepseek\",\n \"description\": \"MCP server for DeepSeek AI with chat, reasoning, sessions, function calling, and cost tracking\",\n \"repository\": {\n \"url\": \"https://github.com/arikusi/deepseek-mcp-server\",\n \"source\": \"github\"\n },\n \"version\": \"2.1.0\",\n \"websiteUrl\": \"https://mcp.tahirl.com\",\n \"icon\": \"https://raw.githubusercontent.com/arikusi/deepseek-mcp-server/main/icon.png\",\n \"packages\": [\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"2.1.0\",\n \"transport\": {\n \"type\": \"stdio\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n },\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"2.1.0\",\n \"transport\": {\n \"type\": \"streamable-http\",\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key (sent as Bearer token)\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n }\n ]\n}\n"}, "c06a79b0b0c950af15b930e9d69b4a02262ae17b": {"t": 1785749506.2092083, "n": 1366, "h": "adcac934fb91e074", "c": "{\n \"$schema\": \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\",\n \"name\": \"io.github.arikusi/deepseek\",\n \"description\": \"MCP server for DeepSeek AI with chat, reasoning, sessions, function calling, and cost tracking\",\n \"repository\": {\n \"url\": \"https://github.com/arikusi/deepseek-mcp-server\",\n \"source\": \"github\"\n },\n \"version\": \"2.0.0\",\n \"websiteUrl\": \"https://mcp.tahirl.com\",\n \"icon\": \"https://raw.githubusercontent.com/arikusi/deepseek-mcp-server/main/icon.png\",\n \"packages\": [\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"2.0.0\",\n \"transport\": {\n \"type\": \"stdio\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n },\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"2.0.0\",\n \"transport\": {\n \"type\": \"streamable-http\",\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key (sent as Bearer token)\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n }\n ]\n}\n"}, "dab07ed93ddde0ab219d4cb7066785847db53a32": {"t": 1785749507.2724462, "n": 1366, "h": "79bceb10487f12ba", "c": "{\n \"$schema\": \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\",\n \"name\": \"io.github.arikusi/deepseek\",\n \"description\": \"MCP server for DeepSeek AI with chat, reasoning, sessions, function calling, and cost tracking\",\n \"repository\": {\n \"url\": \"https://github.com/arikusi/deepseek-mcp-server\",\n \"source\": \"github\"\n },\n \"version\": \"1.8.0\",\n \"websiteUrl\": \"https://mcp.tahirl.com\",\n \"icon\": \"https://raw.githubusercontent.com/arikusi/deepseek-mcp-server/main/icon.png\",\n \"packages\": [\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.8.0\",\n \"transport\": {\n \"type\": \"stdio\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n },\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.8.0\",\n \"transport\": {\n \"type\": \"streamable-http\",\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key (sent as Bearer token)\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n }\n ]\n}\n"}, "9fd514292d23d59ca1434b01f019aca6ef4356f9": {"t": 1785749508.3561347, "n": 1366, "h": "0a5a8c7ff8d22dff", "c": "{\n \"$schema\": \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\",\n \"name\": \"io.github.arikusi/deepseek\",\n \"description\": \"MCP server for DeepSeek AI with chat, reasoning, sessions, function calling, and cost tracking\",\n \"repository\": {\n \"url\": \"https://github.com/arikusi/deepseek-mcp-server\",\n \"source\": \"github\"\n },\n \"version\": \"1.7.0\",\n \"websiteUrl\": \"https://mcp.tahirl.com\",\n \"icon\": \"https://raw.githubusercontent.com/arikusi/deepseek-mcp-server/main/icon.png\",\n \"packages\": [\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.7.0\",\n \"transport\": {\n \"type\": \"stdio\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n },\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.7.0\",\n \"transport\": {\n \"type\": \"streamable-http\",\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key (sent as Bearer token)\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n }\n ]\n}\n"}, "2ca34c8f8e794b091a51ed1c300ea307c6087f79": {"t": 1785749509.44055, "n": 1366, "h": "0ccac5da79800f07", "c": "{\n \"$schema\": \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\",\n \"name\": \"io.github.arikusi/deepseek\",\n \"description\": \"MCP server for DeepSeek AI with chat, reasoning, sessions, function calling, and cost tracking\",\n \"repository\": {\n \"url\": \"https://github.com/arikusi/deepseek-mcp-server\",\n \"source\": \"github\"\n },\n \"version\": \"1.6.0\",\n \"websiteUrl\": \"https://mcp.tahirl.com\",\n \"icon\": \"https://raw.githubusercontent.com/arikusi/deepseek-mcp-server/main/icon.png\",\n \"packages\": [\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.6.0\",\n \"transport\": {\n \"type\": \"stdio\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n },\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.6.0\",\n \"transport\": {\n \"type\": \"streamable-http\",\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key (sent as Bearer token)\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n }\n ]\n}\n"}, "178e63a392f3e73663085bd2aa30f3c12d61189f": {"t": 1785749510.5607846, "n": 1366, "h": "fc2d1a457762cb0f", "c": "{\n \"$schema\": \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\",\n \"name\": \"io.github.arikusi/deepseek\",\n \"description\": \"MCP server for DeepSeek AI with chat, reasoning, sessions, function calling, and cost tracking\",\n \"repository\": {\n \"url\": \"https://github.com/arikusi/deepseek-mcp-server\",\n \"source\": \"github\"\n },\n \"version\": \"1.5.2\",\n \"websiteUrl\": \"https://mcp.tahirl.com\",\n \"icon\": \"https://raw.githubusercontent.com/arikusi/deepseek-mcp-server/main/icon.png\",\n \"packages\": [\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.5.2\",\n \"transport\": {\n \"type\": \"stdio\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n },\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.5.2\",\n \"transport\": {\n \"type\": \"streamable-http\",\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key (sent as Bearer token)\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n }\n ]\n}\n"}, "ef8782e523669c20cd6dc8d694fda55798eb691d": {"t": 1785749511.6381552, "n": 1324, "h": "dda85481ac2dc0e7", "c": "{\n \"$schema\": \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\",\n \"name\": \"io.github.arikusi/deepseek\",\n \"description\": \"MCP server for DeepSeek AI with chat, reasoning, sessions, function calling, and cost tracking\",\n \"repository\": {\n \"url\": \"https://github.com/arikusi/deepseek-mcp-server\",\n \"source\": \"github\"\n },\n \"version\": \"1.5.1\",\n \"icon\": \"https://raw.githubusercontent.com/arikusi/deepseek-mcp-server/main/icon.png\",\n \"packages\": [\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.5.1\",\n \"transport\": {\n \"type\": \"stdio\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n },\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.5.1\",\n \"transport\": {\n \"type\": \"streamable-http\",\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key (sent as Bearer token)\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n }\n ]\n}\n"}, "e03b6e43c654aae907a240c64269238abb292025": {"t": 1785749512.8687837, "n": 1384, "h": "663b9e2109a6d3b9", "c": "{\n \"$schema\": \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\",\n \"name\": \"io.github.arikusi/deepseek\",\n \"description\": \"MCP server for DeepSeek AI — chat, reasoning, multi-turn sessions, function calling, thinking mode, model fallback with circuit breaker, and cost tracking\",\n \"repository\": {\n \"url\": \"https://github.com/arikusi/deepseek-mcp-server\",\n \"source\": \"github\"\n },\n \"version\": \"1.5.1\",\n \"icon\": \"https://raw.githubusercontent.com/arikusi/deepseek-mcp-server/main/icon.png\",\n \"packages\": [\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.5.1\",\n \"transport\": {\n \"type\": \"stdio\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n },\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.5.1\",\n \"transport\": {\n \"type\": \"streamable-http\",\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key (sent as Bearer token)\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n }\n ]\n}\n"}, "401892e47716c4fe1c6cebdcfb7e732103fd4692": {"t": 1785749513.9622648, "n": 1384, "h": "c0e790904deea221", "c": "{\n \"$schema\": \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\",\n \"name\": \"io.github.arikusi/deepseek\",\n \"description\": \"MCP server for DeepSeek AI — chat, reasoning, multi-turn sessions, function calling, thinking mode, model fallback with circuit breaker, and cost tracking\",\n \"repository\": {\n \"url\": \"https://github.com/arikusi/deepseek-mcp-server\",\n \"source\": \"github\"\n },\n \"version\": \"1.5.0\",\n \"icon\": \"https://raw.githubusercontent.com/arikusi/deepseek-mcp-server/main/icon.png\",\n \"packages\": [\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.5.0\",\n \"transport\": {\n \"type\": \"stdio\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n },\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.5.0\",\n \"transport\": {\n \"type\": \"streamable-http\",\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key (sent as Bearer token)\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n }\n ]\n}\n"}, "d6f9b4ff31d863dcc7ae52158967038d1aefc816": {"t": 1785749515.0408843, "n": 1312, "h": "1f22852d2bcbd8d8", "c": "{\n \"$schema\": \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\",\n \"name\": \"io.github.arikusi/deepseek\",\n \"description\": \"MCP server for DeepSeek AI — chat, reasoning, sessions, fallback, function calling\",\n \"repository\": {\n \"url\": \"https://github.com/arikusi/deepseek-mcp-server\",\n \"source\": \"github\"\n },\n \"version\": \"1.5.0\",\n \"icon\": \"https://raw.githubusercontent.com/arikusi/deepseek-mcp-server/main/icon.png\",\n \"packages\": [\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.5.0\",\n \"transport\": {\n \"type\": \"stdio\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n },\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.5.0\",\n \"transport\": {\n \"type\": \"streamable-http\",\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key (sent as Bearer token)\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n }\n ]\n}\n"}, "639aed1e81b3f88270f5fa9fdd9ead0a2ba4b2f3": {"t": 1785749516.1293137, "n": 1223, "h": "51b7308383624b77", "c": "{\n \"$schema\": \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\",\n \"name\": \"io.github.arikusi/deepseek\",\n \"description\": \"MCP server for DeepSeek AI — chat, reasoning, sessions, fallback, function calling\",\n \"repository\": {\n \"url\": \"https://github.com/arikusi/deepseek-mcp-server\",\n \"source\": \"github\"\n },\n \"version\": \"1.5.0\",\n \"packages\": [\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.5.0\",\n \"transport\": {\n \"type\": \"stdio\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n },\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.5.0\",\n \"transport\": {\n \"type\": \"streamable-http\",\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key (sent as Bearer token)\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n }\n ]\n}\n"}, "03f0206a0a6601690b33d2028fb8feec4e8300a9": {"t": 1785749517.1832263, "n": 1216, "h": "723361d198b61b09", "c": "{\n \"$schema\": \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\",\n \"name\": \"io.github.arikusi/deepseek\",\n \"description\": \"MCP server for DeepSeek AI — chat, reasoning, sessions, fallback, function calling\",\n \"repository\": {\n \"url\": \"https://github.com/arikusi/deepseek-mcp-server\",\n \"source\": \"github\"\n },\n \"version\": \"1.5.0\",\n \"packages\": [\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.5.0\",\n \"transport\": {\n \"type\": \"stdio\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n },\n {\n \"registryType\": \"custom\",\n \"identifier\": \"deepseek-mcp-worker\",\n \"version\": \"1.5.0\",\n \"transport\": {\n \"type\": \"streamableHttp\",\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key (sent as Bearer token)\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n }\n ]\n}\n"}, "b0dd00cdfff2f9747b04a967ed1d5babf226f315": {"t": 1785749518.2300477, "n": 762, "h": "e710073fdd0711a7", "c": "{\n \"$schema\": \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\",\n \"name\": \"io.github.arikusi/deepseek\",\n \"description\": \"MCP server for DeepSeek AI — chat, reasoning, sessions, fallback, function calling\",\n \"repository\": {\n \"url\": \"https://github.com/arikusi/deepseek-mcp-server\",\n \"source\": \"github\"\n },\n \"version\": \"1.4.3\",\n \"packages\": [\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.4.3\",\n \"transport\": {\n \"type\": \"stdio\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n }\n ]\n}\n"}, "1f90faae26935a7c16efaaf7e8251d551efae2c0": {"t": 1785749519.2949202, "n": 762, "h": "ddbc08b56aef6542", "c": "{\n \"$schema\": \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\",\n \"name\": \"io.github.arikusi/deepseek\",\n \"description\": \"MCP server for DeepSeek AI — chat, reasoning, sessions, fallback, function calling\",\n \"repository\": {\n \"url\": \"https://github.com/arikusi/deepseek-mcp-server\",\n \"source\": \"github\"\n },\n \"version\": \"1.4.2\",\n \"packages\": [\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.4.2\",\n \"transport\": {\n \"type\": \"stdio\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n }\n ]\n}\n"}, "9e364641ed058d311518ae2261fc5c5d95e34c53": {"t": 1785749520.3796995, "n": 794, "h": "55e2166046dff115", "c": "{\n \"$schema\": \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\",\n \"name\": \"io.github.arikusi/deepseek\",\n \"description\": \"MCP server for DeepSeek AI models — chat, reasoning, sessions, function calling, model fallback, and cost tracking\",\n \"repository\": {\n \"url\": \"https://github.com/arikusi/deepseek-mcp-server\",\n \"source\": \"github\"\n },\n \"version\": \"1.4.2\",\n \"packages\": [\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.4.2\",\n \"transport\": {\n \"type\": \"stdio\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n }\n ]\n}\n"}, "a46f08ced28581052208a6e88a4500a2252f7c1e": {"t": 1785749521.475426, "n": 794, "h": "bdff6c6b1a7bd57f", "c": "{\n \"$schema\": \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\",\n \"name\": \"io.github.arikusi/deepseek\",\n \"description\": \"MCP server for DeepSeek AI models — chat, reasoning, sessions, function calling, model fallback, and cost tracking\",\n \"repository\": {\n \"url\": \"https://github.com/arikusi/deepseek-mcp-server\",\n \"source\": \"github\"\n },\n \"version\": \"1.4.1\",\n \"packages\": [\n {\n \"registryType\": \"npm\",\n \"identifier\": \"@arikusi/deepseek-mcp-server\",\n \"version\": \"1.4.1\",\n \"transport\": {\n \"type\": \"stdio\"\n },\n \"environmentVariables\": [\n {\n \"name\": \"DEEPSEEK_API_KEY\",\n \"description\": \"DeepSeek API key\",\n \"isRequired\": true,\n \"isSecret\": true\n }\n ]\n }\n ]\n}\n"}, "3f1d3d9e6a498db02e60654dfe70fa5fb629e0a0": {"t": 1785749522.7182713, "n": 1880, "h": "b5d4dcedd211c320", "c": "{\n \"$schema\": \"https://raw.githubusercontent.com/modelcontextprotocol/registry/main/schema/server.json\",\n \"name\": \"@arikusi/deepseek-mcp-server\",\n \"description\": \"MCP server for DeepSeek AI models — chat, reasoning, sessions, function calling, model fallback, and cost tracking\",\n \"version\": \"1.4.0\",\n \"repository\": \"https://github.com/arikusi/deepseek-mcp-server\",\n \"homepage\": \"https://github.com/arikusi/deepseek-mcp-server#readme\",\n \"author\": \"arikusi\",\n \"license\": \"MIT\",\n \"transport\": \"stdio\",\n \"runtime\": \"node\",\n \"installation\": {\n \"npm\": \"@arikusi/deepseek-mcp-server\"\n },\n \"configuration\": {\n \"required\": [\"DEEPSEEK_API_KEY\"],\n \"optional\": [\n \"DEEPSEEK_BASE_URL\",\n \"DEFAULT_MODEL\",\n \"SHOW_COST_INFO\",\n \"REQUEST_TIMEOUT\",\n \"MAX_RETRIES\",\n \"SKIP_CONNECTION_TEST\",\n \"MAX_MESSAGE_LENGTH\",\n \"SESSION_TTL_MINUTES\",\n \"MAX_SESSIONS\",\n \"FALLBACK_ENABLED\",\n \"CIRCUIT_BREAKER_THRESHOLD\",\n \"CIRCUIT_BREAKER_RESET_TIMEOUT\",\n \"MAX_SESSION_MESSAGES\",\n \"ENABLE_MULTIMODAL\"\n ]\n },\n \"tools\": [\n {\n \"name\": \"deepseek_chat\",\n \"description\": \"Chat with DeepSeek AI models with sessions, function calling, thinking mode, JSON output, and cost tracking\"\n },\n {\n \"name\": \"deepseek_sessions\",\n \"description\": \"List, delete, or clear conversation sessions\"\n }\n ],\n \"resources\": [\n { \"uri\": \"deepseek://models\", \"description\": \"Available models with capabilities and pricing\" },\n { \"uri\": \"deepseek://config\", \"description\": \"Current server configuration (API key masked)\" },\n { \"uri\": \"deepseek://usage\", \"description\": \"Real-time usage statistics\" }\n ],\n \"prompts\": 12,\n \"keywords\": [\n \"deepseek\",\n \"ai\",\n \"llm\",\n \"mcp\",\n \"chat\",\n \"reasoning\",\n \"function-calling\",\n \"sessions\",\n \"circuit-breaker\",\n \"model-fallback\"\n ]\n}\n"}}, "src/config.ts": {"5eb18b60abee16172cb9d04e70b4c46fd470783a": {"t": 1785745560.9477131, "n": 4673, "h": "06e4646cb9b6e218", "c": "/**\n * Centralized Configuration\n * Loads and validates configuration from environment variables\n */\n\nimport { z } from 'zod';\nimport { ConfigError } from './errors.js';\n\nconst ConfigSchema = z.object({\n apiKey: z.string().min(1, 'DEEPSEEK_API_KEY is required'),\n baseUrl: z.string().url().default('https://api.deepseek.com'),\n showCostInfo: z.boolean().default(true),\n requestTimeout: z.number().positive().default(60000),\n maxRetries: z.number().min(0).max(10).default(2),\n skipConnectionTest: z.boolean().default(false),\n maxMessageLength: z.number().positive().default(100_000),\n sessionTtlMinutes: z.number().positive().default(30),\n maxSessions: z.number().positive().default(100),\n fallbackEnabled: z.boolean().default(true),\n defaultModel: z.string().default('deepseek-v4-flash'),\n circuitBreakerThreshold: z.number().positive().default(5),\n circuitBreakerResetTimeout: z.number().positive().default(30000),\n maxSessionMessages: z.number().positive().default(200),\n enableMultimodal: z.boolean().default(false),\n // Max server-side repair retries when a response_schema validation fails.\n // Set to 0 for a strictly deterministic single call (no extra API round-trips).\n responseSchemaMaxRetries: z.number().min(0).max(5).default(2),\n transport: z.enum(['stdio', 'http']).default('stdio'),\n httpPort: z.number().positive().default(3000),\n httpHost: z.string().min(1).default('127.0.0.1'),\n httpAuthToken: z.string().min(1).optional(),\n httpAllowedHosts: z.array(z.string().min(1)).optional(),\n});\n\nexport type Config = z.infer;\n\nlet cachedConfig: Config | null = null;\n\n/**\n * Load configuration from environment variables.\n * Validates with Zod and caches the result.\n * Throws ConfigError if validation fails.\n */\nexport function loadConfig(): Config {\n const raw = {\n apiKey: process.env.DEEPSEEK_API_KEY || '',\n baseUrl: process.env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com',\n showCostInfo: process.env.SHOW_COST_INFO !== 'false',\n requestTimeout: process.env.REQUEST_TIMEOUT\n ? parseInt(process.env.REQUEST_TIMEOUT, 10)\n : 60000,\n maxRetries: process.env.MAX_RETRIES\n ? parseInt(process.env.MAX_RETRIES, 10)\n : 2,\n skipConnectionTest: process.env.SKIP_CONNECTION_TEST === 'true',\n maxMessageLength: process.env.MAX_MESSAGE_LENGTH\n ? parseInt(process.env.MAX_MESSAGE_LENGTH, 10)\n : 100_000,\n sessionTtlMinutes: process.env.SESSION_TTL_MINUTES\n ? parseInt(process.env.SESSION_TTL_MINUTES, 10)\n : 30,\n maxSessions: process.env.MAX_SESSIONS\n ? parseInt(process.env.MAX_SESSIONS, 10)\n : 100,\n fallbackEnabled: process.env.FALLBACK_ENABLED !== 'false',\n defaultModel: process.env.DEFAULT_MODEL || 'deepseek-v4-flash',\n circuitBreakerThreshold: process.env.CIRCUIT_BREAKER_THRESHOLD\n ? parseInt(process.env.CIRCUIT_BREAKER_THRESHOLD, 10)\n : 5,\n circuitBreakerResetTimeout: process.env.CIRCUIT_BREAKER_RESET_TIMEOUT\n ? parseInt(process.env.CIRCUIT_BREAKER_RESET_TIMEOUT, 10)\n : 30000,\n maxSessionMessages: process.env.MAX_SESSION_MESSAGES\n ? parseInt(process.env.MAX_SESSION_MESSAGES, 10)\n : 200,\n enableMultimodal: process.env.ENABLE_MULTIMODAL === 'true',\n responseSchemaMaxRetries: process.env.RESPONSE_SCHEMA_MAX_RETRIES\n ? parseInt(process.env.RESPONSE_SCHEMA_MAX_RETRIES, 10)\n : 2,\n transport: (process.env.TRANSPORT || 'stdio') as 'stdio' | 'http',\n httpPort: process.env.HTTP_PORT ? parseInt(process.env.HTTP_PORT, 10) : 3000,\n httpHost: process.env.HTTP_HOST || '127.0.0.1',\n httpAuthToken: process.env.HTTP_AUTH_TOKEN || undefined,\n httpAllowedHosts: process.env.HTTP_ALLOWED_HOSTS\n ? process.env.HTTP_ALLOWED_HOSTS.split(',').map((h) => h.trim()).filter(Boolean)\n : undefined,\n };\n\n const result = ConfigSchema.safeParse(raw);\n\n if (!result.success) {\n const issues = result.error.issues.map((issue) => ({\n path: issue.path.join('.'),\n message: issue.message,\n }));\n\n const hint = !raw.apiKey\n ? '\\nPlease set your DeepSeek API key:\\n export DEEPSEEK_API_KEY=\"your-api-key-here\"'\n : '';\n\n throw new ConfigError(\n `Configuration validation failed${hint}`,\n issues\n );\n }\n\n cachedConfig = result.data;\n return cachedConfig;\n}\n\n/**\n * Get the cached configuration.\n * Throws if loadConfig() hasn't been called yet.\n */\nexport function getConfig(): Config {\n if (!cachedConfig) {\n throw new Error('Config not loaded. Call loadConfig() first.');\n }\n return cachedConfig;\n}\n\n/**\n * Reset cached configuration (for testing).\n */\nexport function resetConfig(): void {\n cachedConfig = null;\n}\n"}, "a0a1dc5a7dca41637a8349afe5ac6e6e443199fc": {"t": 1785749500.3258393, "n": 4673, "h": "06e4646cb9b6e218", "c": "/**\n * Centralized Configuration\n * Loads and validates configuration from environment variables\n */\n\nimport { z } from 'zod';\nimport { ConfigError } from './errors.js';\n\nconst ConfigSchema = z.object({\n apiKey: z.string().min(1, 'DEEPSEEK_API_KEY is required'),\n baseUrl: z.string().url().default('https://api.deepseek.com'),\n showCostInfo: z.boolean().default(true),\n requestTimeout: z.number().positive().default(60000),\n maxRetries: z.number().min(0).max(10).default(2),\n skipConnectionTest: z.boolean().default(false),\n maxMessageLength: z.number().positive().default(100_000),\n sessionTtlMinutes: z.number().positive().default(30),\n maxSessions: z.number().positive().default(100),\n fallbackEnabled: z.boolean().default(true),\n defaultModel: z.string().default('deepseek-v4-flash'),\n circuitBreakerThreshold: z.number().positive().default(5),\n circuitBreakerResetTimeout: z.number().positive().default(30000),\n maxSessionMessages: z.number().positive().default(200),\n enableMultimodal: z.boolean().default(false),\n // Max server-side repair retries when a response_schema validation fails.\n // Set to 0 for a strictly deterministic single call (no extra API round-trips).\n responseSchemaMaxRetries: z.number().min(0).max(5).default(2),\n transport: z.enum(['stdio', 'http']).default('stdio'),\n httpPort: z.number().positive().default(3000),\n httpHost: z.string().min(1).default('127.0.0.1'),\n httpAuthToken: z.string().min(1).optional(),\n httpAllowedHosts: z.array(z.string().min(1)).optional(),\n});\n\nexport type Config = z.infer;\n\nlet cachedConfig: Config | null = null;\n\n/**\n * Load configuration from environment variables.\n * Validates with Zod and caches the result.\n * Throws ConfigError if validation fails.\n */\nexport function loadConfig(): Config {\n const raw = {\n apiKey: process.env.DEEPSEEK_API_KEY || '',\n baseUrl: process.env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com',\n showCostInfo: process.env.SHOW_COST_INFO !== 'false',\n requestTimeout: process.env.REQUEST_TIMEOUT\n ? parseInt(process.env.REQUEST_TIMEOUT, 10)\n : 60000,\n maxRetries: process.env.MAX_RETRIES\n ? parseInt(process.env.MAX_RETRIES, 10)\n : 2,\n skipConnectionTest: process.env.SKIP_CONNECTION_TEST === 'true',\n maxMessageLength: process.env.MAX_MESSAGE_LENGTH\n ? parseInt(process.env.MAX_MESSAGE_LENGTH, 10)\n : 100_000,\n sessionTtlMinutes: process.env.SESSION_TTL_MINUTES\n ? parseInt(process.env.SESSION_TTL_MINUTES, 10)\n : 30,\n maxSessions: process.env.MAX_SESSIONS\n ? parseInt(process.env.MAX_SESSIONS, 10)\n : 100,\n fallbackEnabled: process.env.FALLBACK_ENABLED !== 'false',\n defaultModel: process.env.DEFAULT_MODEL || 'deepseek-v4-flash',\n circuitBreakerThreshold: process.env.CIRCUIT_BREAKER_THRESHOLD\n ? parseInt(process.env.CIRCUIT_BREAKER_THRESHOLD, 10)\n : 5,\n circuitBreakerResetTimeout: process.env.CIRCUIT_BREAKER_RESET_TIMEOUT\n ? parseInt(process.env.CIRCUIT_BREAKER_RESET_TIMEOUT, 10)\n : 30000,\n maxSessionMessages: process.env.MAX_SESSION_MESSAGES\n ? parseInt(process.env.MAX_SESSION_MESSAGES, 10)\n : 200,\n enableMultimodal: process.env.ENABLE_MULTIMODAL === 'true',\n responseSchemaMaxRetries: process.env.RESPONSE_SCHEMA_MAX_RETRIES\n ? parseInt(process.env.RESPONSE_SCHEMA_MAX_RETRIES, 10)\n : 2,\n transport: (process.env.TRANSPORT || 'stdio') as 'stdio' | 'http',\n httpPort: process.env.HTTP_PORT ? parseInt(process.env.HTTP_PORT, 10) : 3000,\n httpHost: process.env.HTTP_HOST || '127.0.0.1',\n httpAuthToken: process.env.HTTP_AUTH_TOKEN || undefined,\n httpAllowedHosts: process.env.HTTP_ALLOWED_HOSTS\n ? process.env.HTTP_ALLOWED_HOSTS.split(',').map((h) => h.trim()).filter(Boolean)\n : undefined,\n };\n\n const result = ConfigSchema.safeParse(raw);\n\n if (!result.success) {\n const issues = result.error.issues.map((issue) => ({\n path: issue.path.join('.'),\n message: issue.message,\n }));\n\n const hint = !raw.apiKey\n ? '\\nPlease set your DeepSeek API key:\\n export DEEPSEEK_API_KEY=\"your-api-key-here\"'\n : '';\n\n throw new ConfigError(\n `Configuration validation failed${hint}`,\n issues\n );\n }\n\n cachedConfig = result.data;\n return cachedConfig;\n}\n\n/**\n * Get the cached configuration.\n * Throws if loadConfig() hasn't been called yet.\n */\nexport function getConfig(): Config {\n if (!cachedConfig) {\n throw new Error('Config not loaded. Call loadConfig() first.');\n }\n return cachedConfig;\n}\n\n/**\n * Reset cached configuration (for testing).\n */\nexport function resetConfig(): void {\n cachedConfig = null;\n}\n"}, "c06a79b0b0c950af15b930e9d69b4a02262ae17b": {"t": 1785749502.455241, "n": 4305, "h": "5252dadae042739e", "c": "/**\n * Centralized Configuration\n * Loads and validates configuration from environment variables\n */\n\nimport { z } from 'zod';\nimport { ConfigError } from './errors.js';\n\nconst ConfigSchema = z.object({\n apiKey: z.string().min(1, 'DEEPSEEK_API_KEY is required'),\n baseUrl: z.string().url().default('https://api.deepseek.com'),\n showCostInfo: z.boolean().default(true),\n requestTimeout: z.number().positive().default(60000),\n maxRetries: z.number().min(0).max(10).default(2),\n skipConnectionTest: z.boolean().default(false),\n maxMessageLength: z.number().positive().default(100_000),\n sessionTtlMinutes: z.number().positive().default(30),\n maxSessions: z.number().positive().default(100),\n fallbackEnabled: z.boolean().default(true),\n defaultModel: z.string().default('deepseek-v4-flash'),\n circuitBreakerThreshold: z.number().positive().default(5),\n circuitBreakerResetTimeout: z.number().positive().default(30000),\n maxSessionMessages: z.number().positive().default(200),\n enableMultimodal: z.boolean().default(false),\n transport: z.enum(['stdio', 'http']).default('stdio'),\n httpPort: z.number().positive().default(3000),\n httpHost: z.string().min(1).default('127.0.0.1'),\n httpAuthToken: z.string().min(1).optional(),\n httpAllowedHosts: z.array(z.string().min(1)).optional(),\n});\n\nexport type Config = z.infer;\n\nlet cachedConfig: Config | null = null;\n\n/**\n * Load configuration from environment variables.\n * Validates with Zod and caches the result.\n * Throws ConfigError if validation fails.\n */\nexport function loadConfig(): Config {\n const raw = {\n apiKey: process.env.DEEPSEEK_API_KEY || '',\n baseUrl: process.env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com',\n showCostInfo: process.env.SHOW_COST_INFO !== 'false',\n requestTimeout: process.env.REQUEST_TIMEOUT\n ? parseInt(process.env.REQUEST_TIMEOUT, 10)\n : 60000,\n maxRetries: process.env.MAX_RETRIES\n ? parseInt(process.env.MAX_RETRIES, 10)\n : 2,\n skipConnectionTest: process.env.SKIP_CONNECTION_TEST === 'true',\n maxMessageLength: process.env.MAX_MESSAGE_LENGTH\n ? parseInt(process.env.MAX_MESSAGE_LENGTH, 10)\n : 100_000,\n sessionTtlMinutes: process.env.SESSION_TTL_MINUTES\n ? parseInt(process.env.SESSION_TTL_MINUTES, 10)\n : 30,\n maxSessions: process.env.MAX_SESSIONS\n ? parseInt(process.env.MAX_SESSIONS, 10)\n : 100,\n fallbackEnabled: process.env.FALLBACK_ENABLED !== 'false',\n defaultModel: process.env.DEFAULT_MODEL || 'deepseek-v4-flash',\n circuitBreakerThreshold: process.env.CIRCUIT_BREAKER_THRESHOLD\n ? parseInt(process.env.CIRCUIT_BREAKER_THRESHOLD, 10)\n : 5,\n circuitBreakerResetTimeout: process.env.CIRCUIT_BREAKER_RESET_TIMEOUT\n ? parseInt(process.env.CIRCUIT_BREAKER_RESET_TIMEOUT, 10)\n : 30000,\n maxSessionMessages: process.env.MAX_SESSION_MESSAGES\n ? parseInt(process.env.MAX_SESSION_MESSAGES, 10)\n : 200,\n enableMultimodal: process.env.ENABLE_MULTIMODAL === 'true',\n transport: (process.env.TRANSPORT || 'stdio') as 'stdio' | 'http',\n httpPort: process.env.HTTP_PORT ? parseInt(process.env.HTTP_PORT, 10) : 3000,\n httpHost: process.env.HTTP_HOST || '127.0.0.1',\n httpAuthToken: process.env.HTTP_AUTH_TOKEN || undefined,\n httpAllowedHosts: process.env.HTTP_ALLOWED_HOSTS\n ? process.env.HTTP_ALLOWED_HOSTS.split(',').map((h) => h.trim()).filter(Boolean)\n : undefined,\n };\n\n const result = ConfigSchema.safeParse(raw);\n\n if (!result.success) {\n const issues = result.error.issues.map((issue) => ({\n path: issue.path.join('.'),\n message: issue.message,\n }));\n\n const hint = !raw.apiKey\n ? '\\nPlease set your DeepSeek API key:\\n export DEEPSEEK_API_KEY=\"your-api-key-here\"'\n : '';\n\n throw new ConfigError(\n `Configuration validation failed${hint}`,\n issues\n );\n }\n\n cachedConfig = result.data;\n return cachedConfig;\n}\n\n/**\n * Get the cached configuration.\n * Throws if loadConfig() hasn't been called yet.\n */\nexport function getConfig(): Config {\n if (!cachedConfig) {\n throw new Error('Config not loaded. Call loadConfig() first.');\n }\n return cachedConfig;\n}\n\n/**\n * Reset cached configuration (for testing).\n */\nexport function resetConfig(): void {\n cachedConfig = null;\n}\n"}, "dab07ed93ddde0ab219d4cb7066785847db53a32": {"t": 1785749503.822055, "n": 4297, "h": "76055bca464bf707", "c": "/**\n * Centralized Configuration\n * Loads and validates configuration from environment variables\n */\n\nimport { z } from 'zod';\nimport { ConfigError } from './errors.js';\n\nconst ConfigSchema = z.object({\n apiKey: z.string().min(1, 'DEEPSEEK_API_KEY is required'),\n baseUrl: z.string().url().default('https://api.deepseek.com'),\n showCostInfo: z.boolean().default(true),\n requestTimeout: z.number().positive().default(60000),\n maxRetries: z.number().min(0).max(10).default(2),\n skipConnectionTest: z.boolean().default(false),\n maxMessageLength: z.number().positive().default(100_000),\n sessionTtlMinutes: z.number().positive().default(30),\n maxSessions: z.number().positive().default(100),\n fallbackEnabled: z.boolean().default(true),\n defaultModel: z.string().default('deepseek-chat'),\n circuitBreakerThreshold: z.number().positive().default(5),\n circuitBreakerResetTimeout: z.number().positive().default(30000),\n maxSessionMessages: z.number().positive().default(200),\n enableMultimodal: z.boolean().default(false),\n transport: z.enum(['stdio', 'http']).default('stdio'),\n httpPort: z.number().positive().default(3000),\n httpHost: z.string().min(1).default('127.0.0.1'),\n httpAuthToken: z.string().min(1).optional(),\n httpAllowedHosts: z.array(z.string().min(1)).optional(),\n});\n\nexport type Config = z.infer;\n\nlet cachedConfig: Config | null = null;\n\n/**\n * Load configuration from environment variables.\n * Validates with Zod and caches the result.\n * Throws ConfigError if validation fails.\n */\nexport function loadConfig(): Config {\n const raw = {\n apiKey: process.env.DEEPSEEK_API_KEY || '',\n baseUrl: process.env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com',\n showCostInfo: process.env.SHOW_COST_INFO !== 'false',\n requestTimeout: process.env.REQUEST_TIMEOUT\n ? parseInt(process.env.REQUEST_TIMEOUT, 10)\n : 60000,\n maxRetries: process.env.MAX_RETRIES\n ? parseInt(process.env.MAX_RETRIES, 10)\n : 2,\n skipConnectionTest: process.env.SKIP_CONNECTION_TEST === 'true',\n maxMessageLength: process.env.MAX_MESSAGE_LENGTH\n ? parseInt(process.env.MAX_MESSAGE_LENGTH, 10)\n : 100_000,\n sessionTtlMinutes: process.env.SESSION_TTL_MINUTES\n ? parseInt(process.env.SESSION_TTL_MINUTES, 10)\n : 30,\n maxSessions: process.env.MAX_SESSIONS\n ? parseInt(process.env.MAX_SESSIONS, 10)\n : 100,\n fallbackEnabled: process.env.FALLBACK_ENABLED !== 'false',\n defaultModel: process.env.DEFAULT_MODEL || 'deepseek-chat',\n circuitBreakerThreshold: process.env.CIRCUIT_BREAKER_THRESHOLD\n ? parseInt(process.env.CIRCUIT_BREAKER_THRESHOLD, 10)\n : 5,\n circuitBreakerResetTimeout: process.env.CIRCUIT_BREAKER_RESET_TIMEOUT\n ? parseInt(process.env.CIRCUIT_BREAKER_RESET_TIMEOUT, 10)\n : 30000,\n maxSessionMessages: process.env.MAX_SESSION_MESSAGES\n ? parseInt(process.env.MAX_SESSION_MESSAGES, 10)\n : 200,\n enableMultimodal: process.env.ENABLE_MULTIMODAL === 'true',\n transport: (process.env.TRANSPORT || 'stdio') as 'stdio' | 'http',\n httpPort: process.env.HTTP_PORT ? parseInt(process.env.HTTP_PORT, 10) : 3000,\n httpHost: process.env.HTTP_HOST || '127.0.0.1',\n httpAuthToken: process.env.HTTP_AUTH_TOKEN || undefined,\n httpAllowedHosts: process.env.HTTP_ALLOWED_HOSTS\n ? process.env.HTTP_ALLOWED_HOSTS.split(',').map((h) => h.trim()).filter(Boolean)\n : undefined,\n };\n\n const result = ConfigSchema.safeParse(raw);\n\n if (!result.success) {\n const issues = result.error.issues.map((issue) => ({\n path: issue.path.join('.'),\n message: issue.message,\n }));\n\n const hint = !raw.apiKey\n ? '\\nPlease set your DeepSeek API key:\\n export DEEPSEEK_API_KEY=\"your-api-key-here\"'\n : '';\n\n throw new ConfigError(\n `Configuration validation failed${hint}`,\n issues\n );\n }\n\n cachedConfig = result.data;\n return cachedConfig;\n}\n\n/**\n * Get the cached configuration.\n * Throws if loadConfig() hasn't been called yet.\n */\nexport function getConfig(): Config {\n if (!cachedConfig) {\n throw new Error('Config not loaded. Call loadConfig() first.');\n }\n return cachedConfig;\n}\n\n/**\n * Reset cached configuration (for testing).\n */\nexport function resetConfig(): void {\n cachedConfig = null;\n}\n"}, "9e364641ed058d311518ae2261fc5c5d95e34c53": {"t": 1785749504.900252, "n": 3867, "h": "0e4f547cf5632ccf", "c": "/**\n * Centralized Configuration\n * Loads and validates configuration from environment variables\n */\n\nimport { z } from 'zod';\nimport { ConfigError } from './errors.js';\n\nconst ConfigSchema = z.object({\n apiKey: z.string().min(1, 'DEEPSEEK_API_KEY is required'),\n baseUrl: z.string().url().default('https://api.deepseek.com'),\n showCostInfo: z.boolean().default(true),\n requestTimeout: z.number().positive().default(60000),\n maxRetries: z.number().min(0).max(10).default(2),\n skipConnectionTest: z.boolean().default(false),\n maxMessageLength: z.number().positive().default(100_000),\n sessionTtlMinutes: z.number().positive().default(30),\n maxSessions: z.number().positive().default(100),\n fallbackEnabled: z.boolean().default(true),\n defaultModel: z.string().default('deepseek-chat'),\n circuitBreakerThreshold: z.number().positive().default(5),\n circuitBreakerResetTimeout: z.number().positive().default(30000),\n maxSessionMessages: z.number().positive().default(200),\n enableMultimodal: z.boolean().default(false),\n transport: z.enum(['stdio', 'http']).default('stdio'),\n httpPort: z.number().positive().default(3000),\n});\n\nexport type Config = z.infer;\n\nlet cachedConfig: Config | null = null;\n\n/**\n * Load configuration from environment variables.\n * Validates with Zod and caches the result.\n * Throws ConfigError if validation fails.\n */\nexport function loadConfig(): Config {\n const raw = {\n apiKey: process.env.DEEPSEEK_API_KEY || '',\n baseUrl: process.env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com',\n showCostInfo: process.env.SHOW_COST_INFO !== 'false',\n requestTimeout: process.env.REQUEST_TIMEOUT\n ? parseInt(process.env.REQUEST_TIMEOUT, 10)\n : 60000,\n maxRetries: process.env.MAX_RETRIES\n ? parseInt(process.env.MAX_RETRIES, 10)\n : 2,\n skipConnectionTest: process.env.SKIP_CONNECTION_TEST === 'true',\n maxMessageLength: process.env.MAX_MESSAGE_LENGTH\n ? parseInt(process.env.MAX_MESSAGE_LENGTH, 10)\n : 100_000,\n sessionTtlMinutes: process.env.SESSION_TTL_MINUTES\n ? parseInt(process.env.SESSION_TTL_MINUTES, 10)\n : 30,\n maxSessions: process.env.MAX_SESSIONS\n ? parseInt(process.env.MAX_SESSIONS, 10)\n : 100,\n fallbackEnabled: process.env.FALLBACK_ENABLED !== 'false',\n defaultModel: process.env.DEFAULT_MODEL || 'deepseek-chat',\n circuitBreakerThreshold: process.env.CIRCUIT_BREAKER_THRESHOLD\n ? parseInt(process.env.CIRCUIT_BREAKER_THRESHOLD, 10)\n : 5,\n circuitBreakerResetTimeout: process.env.CIRCUIT_BREAKER_RESET_TIMEOUT\n ? parseInt(process.env.CIRCUIT_BREAKER_RESET_TIMEOUT, 10)\n : 30000,\n maxSessionMessages: process.env.MAX_SESSION_MESSAGES\n ? parseInt(process.env.MAX_SESSION_MESSAGES, 10)\n : 200,\n enableMultimodal: process.env.ENABLE_MULTIMODAL === 'true',\n transport: (process.env.TRANSPORT || 'stdio') as 'stdio' | 'http',\n httpPort: process.env.HTTP_PORT ? parseInt(process.env.HTTP_PORT, 10) : 3000,\n };\n\n const result = ConfigSchema.safeParse(raw);\n\n if (!result.success) {\n const issues = result.error.issues.map((issue) => ({\n path: issue.path.join('.'),\n message: issue.message,\n }));\n\n const hint = !raw.apiKey\n ? '\\nPlease set your DeepSeek API key:\\n export DEEPSEEK_API_KEY=\"your-api-key-here\"'\n : '';\n\n throw new ConfigError(\n `Configuration validation failed${hint}`,\n issues\n );\n }\n\n cachedConfig = result.data;\n return cachedConfig;\n}\n\n/**\n * Get the cached configuration.\n * Throws if loadConfig() hasn't been called yet.\n */\nexport function getConfig(): Config {\n if (!cachedConfig) {\n throw new Error('Config not loaded. Call loadConfig() first.');\n }\n return cachedConfig;\n}\n\n/**\n * Reset cached configuration (for testing).\n */\nexport function resetConfig(): void {\n cachedConfig = null;\n}\n"}, "3f1d3d9e6a498db02e60654dfe70fa5fb629e0a0": {"t": 1785749505.991702, "n": 3608, "h": "e1fa1a7a800be491", "c": "/**\n * Centralized Configuration\n * Loads and validates configuration from environment variables\n */\n\nimport { z } from 'zod';\nimport { ConfigError } from './errors.js';\n\nconst ConfigSchema = z.object({\n apiKey: z.string().min(1, 'DEEPSEEK_API_KEY is required'),\n baseUrl: z.string().url().default('https://api.deepseek.com'),\n showCostInfo: z.boolean().default(true),\n requestTimeout: z.number().positive().default(60000),\n maxRetries: z.number().min(0).max(10).default(2),\n skipConnectionTest: z.boolean().default(false),\n maxMessageLength: z.number().positive().default(100_000),\n sessionTtlMinutes: z.number().positive().default(30),\n maxSessions: z.number().positive().default(100),\n fallbackEnabled: z.boolean().default(true),\n defaultModel: z.string().default('deepseek-chat'),\n circuitBreakerThreshold: z.number().positive().default(5),\n circuitBreakerResetTimeout: z.number().positive().default(30000),\n maxSessionMessages: z.number().positive().default(200),\n enableMultimodal: z.boolean().default(false),\n});\n\nexport type Config = z.infer;\n\nlet cachedConfig: Config | null = null;\n\n/**\n * Load configuration from environment variables.\n * Validates with Zod and caches the result.\n * Throws ConfigError if validation fails.\n */\nexport function loadConfig(): Config {\n const raw = {\n apiKey: process.env.DEEPSEEK_API_KEY || '',\n baseUrl: process.env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com',\n showCostInfo: process.env.SHOW_COST_INFO !== 'false',\n requestTimeout: process.env.REQUEST_TIMEOUT\n ? parseInt(process.env.REQUEST_TIMEOUT, 10)\n : 60000,\n maxRetries: process.env.MAX_RETRIES\n ? parseInt(process.env.MAX_RETRIES, 10)\n : 2,\n skipConnectionTest: process.env.SKIP_CONNECTION_TEST === 'true',\n maxMessageLength: process.env.MAX_MESSAGE_LENGTH\n ? parseInt(process.env.MAX_MESSAGE_LENGTH, 10)\n : 100_000,\n sessionTtlMinutes: process.env.SESSION_TTL_MINUTES\n ? parseInt(process.env.SESSION_TTL_MINUTES, 10)\n : 30,\n maxSessions: process.env.MAX_SESSIONS\n ? parseInt(process.env.MAX_SESSIONS, 10)\n : 100,\n fallbackEnabled: process.env.FALLBACK_ENABLED !== 'false',\n defaultModel: process.env.DEFAULT_MODEL || 'deepseek-chat',\n circuitBreakerThreshold: process.env.CIRCUIT_BREAKER_THRESHOLD\n ? parseInt(process.env.CIRCUIT_BREAKER_THRESHOLD, 10)\n : 5,\n circuitBreakerResetTimeout: process.env.CIRCUIT_BREAKER_RESET_TIMEOUT\n ? parseInt(process.env.CIRCUIT_BREAKER_RESET_TIMEOUT, 10)\n : 30000,\n maxSessionMessages: process.env.MAX_SESSION_MESSAGES\n ? parseInt(process.env.MAX_SESSION_MESSAGES, 10)\n : 200,\n enableMultimodal: process.env.ENABLE_MULTIMODAL === 'true',\n };\n\n const result = ConfigSchema.safeParse(raw);\n\n if (!result.success) {\n const issues = result.error.issues.map((issue) => ({\n path: issue.path.join('.'),\n message: issue.message,\n }));\n\n const hint = !raw.apiKey\n ? '\\nPlease set your DeepSeek API key:\\n export DEEPSEEK_API_KEY=\"your-api-key-here\"'\n : '';\n\n throw new ConfigError(\n `Configuration validation failed${hint}`,\n issues\n );\n }\n\n cachedConfig = result.data;\n return cachedConfig;\n}\n\n/**\n * Get the cached configuration.\n * Throws if loadConfig() hasn't been called yet.\n */\nexport function getConfig(): Config {\n if (!cachedConfig) {\n throw new Error('Config not loaded. Call loadConfig() first.');\n }\n return cachedConfig;\n}\n\n/**\n * Reset cached configuration (for testing).\n */\nexport function resetConfig(): void {\n cachedConfig = null;\n}\n"}, "d9a2fab03326ab6f722658de2c8b7461433f2ea9": {"t": 1785749507.128902, "n": 3496, "h": "0aed06495e6fcbd2", "c": "/**\n * Centralized Configuration\n * Loads and validates configuration from environment variables\n */\n\nimport { z } from 'zod';\nimport { ConfigError } from './errors.js';\n\nconst ConfigSchema = z.object({\n apiKey: z.string().min(1, 'DEEPSEEK_API_KEY is required'),\n baseUrl: z.string().url().default('https://api.deepseek.com'),\n showCostInfo: z.boolean().default(true),\n requestTimeout: z.number().positive().default(60000),\n maxRetries: z.number().min(0).max(10).default(2),\n skipConnectionTest: z.boolean().default(false),\n maxMessageLength: z.number().positive().default(100_000),\n sessionTtlMinutes: z.number().positive().default(30),\n maxSessions: z.number().positive().default(100),\n fallbackEnabled: z.boolean().default(true),\n defaultModel: z.string().default('deepseek-chat'),\n circuitBreakerThreshold: z.number().positive().default(5),\n circuitBreakerResetTimeout: z.number().positive().default(30000),\n maxSessionMessages: z.number().positive().default(200),\n});\n\nexport type Config = z.infer;\n\nlet cachedConfig: Config | null = null;\n\n/**\n * Load configuration from environment variables.\n * Validates with Zod and caches the result.\n * Throws ConfigError if validation fails.\n */\nexport function loadConfig(): Config {\n const raw = {\n apiKey: process.env.DEEPSEEK_API_KEY || '',\n baseUrl: process.env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com',\n showCostInfo: process.env.SHOW_COST_INFO !== 'false',\n requestTimeout: process.env.REQUEST_TIMEOUT\n ? parseInt(process.env.REQUEST_TIMEOUT, 10)\n : 60000,\n maxRetries: process.env.MAX_RETRIES\n ? parseInt(process.env.MAX_RETRIES, 10)\n : 2,\n skipConnectionTest: process.env.SKIP_CONNECTION_TEST === 'true',\n maxMessageLength: process.env.MAX_MESSAGE_LENGTH\n ? parseInt(process.env.MAX_MESSAGE_LENGTH, 10)\n : 100_000,\n sessionTtlMinutes: process.env.SESSION_TTL_MINUTES\n ? parseInt(process.env.SESSION_TTL_MINUTES, 10)\n : 30,\n maxSessions: process.env.MAX_SESSIONS\n ? parseInt(process.env.MAX_SESSIONS, 10)\n : 100,\n fallbackEnabled: process.env.FALLBACK_ENABLED !== 'false',\n defaultModel: process.env.DEFAULT_MODEL || 'deepseek-chat',\n circuitBreakerThreshold: process.env.CIRCUIT_BREAKER_THRESHOLD\n ? parseInt(process.env.CIRCUIT_BREAKER_THRESHOLD, 10)\n : 5,\n circuitBreakerResetTimeout: process.env.CIRCUIT_BREAKER_RESET_TIMEOUT\n ? parseInt(process.env.CIRCUIT_BREAKER_RESET_TIMEOUT, 10)\n : 30000,\n maxSessionMessages: process.env.MAX_SESSION_MESSAGES\n ? parseInt(process.env.MAX_SESSION_MESSAGES, 10)\n : 200,\n };\n\n const result = ConfigSchema.safeParse(raw);\n\n if (!result.success) {\n const issues = result.error.issues.map((issue) => ({\n path: issue.path.join('.'),\n message: issue.message,\n }));\n\n const hint = !raw.apiKey\n ? '\\nPlease set your DeepSeek API key:\\n export DEEPSEEK_API_KEY=\"your-api-key-here\"'\n : '';\n\n throw new ConfigError(\n `Configuration validation failed${hint}`,\n issues\n );\n }\n\n cachedConfig = result.data;\n return cachedConfig;\n}\n\n/**\n * Get the cached configuration.\n * Throws if loadConfig() hasn't been called yet.\n */\nexport function getConfig(): Config {\n if (!cachedConfig) {\n throw new Error('Config not loaded. Call loadConfig() first.');\n }\n return cachedConfig;\n}\n\n/**\n * Reset cached configuration (for testing).\n */\nexport function resetConfig(): void {\n cachedConfig = null;\n}\n"}, "014950567ad984191b0b4a34f539fbcf40551b41": {"t": 1785749508.2164352, "n": 2893, "h": "a0926be02268e74e", "c": "/**\n * Centralized Configuration\n * Loads and validates configuration from environment variables\n */\n\nimport { z } from 'zod';\nimport { ConfigError } from './errors.js';\n\nconst ConfigSchema = z.object({\n apiKey: z.string().min(1, 'DEEPSEEK_API_KEY is required'),\n baseUrl: z.string().url().default('https://api.deepseek.com'),\n showCostInfo: z.boolean().default(true),\n requestTimeout: z.number().positive().default(60000),\n maxRetries: z.number().min(0).max(10).default(2),\n skipConnectionTest: z.boolean().default(false),\n maxMessageLength: z.number().positive().default(100_000),\n sessionTtlMinutes: z.number().positive().default(30),\n maxSessions: z.number().positive().default(100),\n fallbackEnabled: z.boolean().default(true),\n defaultModel: z.string().default('deepseek-chat'),\n});\n\nexport type Config = z.infer;\n\nlet cachedConfig: Config | null = null;\n\n/**\n * Load configuration from environment variables.\n * Validates with Zod and caches the result.\n * Throws ConfigError if validation fails.\n */\nexport function loadConfig(): Config {\n const raw = {\n apiKey: process.env.DEEPSEEK_API_KEY || '',\n baseUrl: process.env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com',\n showCostInfo: process.env.SHOW_COST_INFO !== 'false',\n requestTimeout: process.env.REQUEST_TIMEOUT\n ? parseInt(process.env.REQUEST_TIMEOUT, 10)\n : 60000,\n maxRetries: process.env.MAX_RETRIES\n ? parseInt(process.env.MAX_RETRIES, 10)\n : 2,\n skipConnectionTest: process.env.SKIP_CONNECTION_TEST === 'true',\n maxMessageLength: process.env.MAX_MESSAGE_LENGTH\n ? parseInt(process.env.MAX_MESSAGE_LENGTH, 10)\n : 100_000,\n sessionTtlMinutes: process.env.SESSION_TTL_MINUTES\n ? parseInt(process.env.SESSION_TTL_MINUTES, 10)\n : 30,\n maxSessions: process.env.MAX_SESSIONS\n ? parseInt(process.env.MAX_SESSIONS, 10)\n : 100,\n fallbackEnabled: process.env.FALLBACK_ENABLED !== 'false',\n defaultModel: process.env.DEFAULT_MODEL || 'deepseek-chat',\n };\n\n const result = ConfigSchema.safeParse(raw);\n\n if (!result.success) {\n const issues = result.error.issues.map((issue) => ({\n path: issue.path.join('.'),\n message: issue.message,\n }));\n\n const hint = !raw.apiKey\n ? '\\nPlease set your DeepSeek API key:\\n export DEEPSEEK_API_KEY=\"your-api-key-here\"'\n : '';\n\n throw new ConfigError(\n `Configuration validation failed${hint}`,\n issues\n );\n }\n\n cachedConfig = result.data;\n return cachedConfig;\n}\n\n/**\n * Get the cached configuration.\n * Throws if loadConfig() hasn't been called yet.\n */\nexport function getConfig(): Config {\n if (!cachedConfig) {\n throw new Error('Config not loaded. Call loadConfig() first.');\n }\n return cachedConfig;\n}\n\n/**\n * Reset cached configuration (for testing).\n */\nexport function resetConfig(): void {\n cachedConfig = null;\n}\n"}, "d08fbeadacd6deb176fba497c16e216536124a8d": {"t": 1785749509.2967856, "n": 2776, "h": "6a88dd21f7e5de3d", "c": "/**\n * Centralized Configuration\n * Loads and validates configuration from environment variables\n */\n\nimport { z } from 'zod';\nimport { ConfigError } from './errors.js';\n\nconst ConfigSchema = z.object({\n apiKey: z.string().min(1, 'DEEPSEEK_API_KEY is required'),\n baseUrl: z.string().url().default('https://api.deepseek.com'),\n showCostInfo: z.boolean().default(true),\n requestTimeout: z.number().positive().default(60000),\n maxRetries: z.number().min(0).max(10).default(2),\n skipConnectionTest: z.boolean().default(false),\n maxMessageLength: z.number().positive().default(100_000),\n sessionTtlMinutes: z.number().positive().default(30),\n maxSessions: z.number().positive().default(100),\n fallbackEnabled: z.boolean().default(true),\n});\n\nexport type Config = z.infer;\n\nlet cachedConfig: Config | null = null;\n\n/**\n * Load configuration from environment variables.\n * Validates with Zod and caches the result.\n * Throws ConfigError if validation fails.\n */\nexport function loadConfig(): Config {\n const raw = {\n apiKey: process.env.DEEPSEEK_API_KEY || '',\n baseUrl: process.env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com',\n showCostInfo: process.env.SHOW_COST_INFO !== 'false',\n requestTimeout: process.env.REQUEST_TIMEOUT\n ? parseInt(process.env.REQUEST_TIMEOUT, 10)\n : 60000,\n maxRetries: process.env.MAX_RETRIES\n ? parseInt(process.env.MAX_RETRIES, 10)\n : 2,\n skipConnectionTest: process.env.SKIP_CONNECTION_TEST === 'true',\n maxMessageLength: process.env.MAX_MESSAGE_LENGTH\n ? parseInt(process.env.MAX_MESSAGE_LENGTH, 10)\n : 100_000,\n sessionTtlMinutes: process.env.SESSION_TTL_MINUTES\n ? parseInt(process.env.SESSION_TTL_MINUTES, 10)\n : 30,\n maxSessions: process.env.MAX_SESSIONS\n ? parseInt(process.env.MAX_SESSIONS, 10)\n : 100,\n fallbackEnabled: process.env.FALLBACK_ENABLED !== 'false',\n };\n\n const result = ConfigSchema.safeParse(raw);\n\n if (!result.success) {\n const issues = result.error.issues.map((issue) => ({\n path: issue.path.join('.'),\n message: issue.message,\n }));\n\n const hint = !raw.apiKey\n ? '\\nPlease set your DeepSeek API key:\\n export DEEPSEEK_API_KEY=\"your-api-key-here\"'\n : '';\n\n throw new ConfigError(\n `Configuration validation failed${hint}`,\n issues\n );\n }\n\n cachedConfig = result.data;\n return cachedConfig;\n}\n\n/**\n * Get the cached configuration.\n * Throws if loadConfig() hasn't been called yet.\n */\nexport function getConfig(): Config {\n if (!cachedConfig) {\n throw new Error('Config not loaded. Call loadConfig() first.');\n }\n return cachedConfig;\n}\n\n/**\n * Reset cached configuration (for testing).\n */\nexport function resetConfig(): void {\n cachedConfig = null;\n}\n"}, "440b7099f12fcab015d490983725d13e305ddc76": {"t": 1785749510.3702173, "n": 2337, "h": "fadb77ff18d539c3", "c": "/**\n * Centralized Configuration\n * Loads and validates configuration from environment variables\n */\n\nimport { z } from 'zod';\nimport { ConfigError } from './errors.js';\n\nconst ConfigSchema = z.object({\n apiKey: z.string().min(1, 'DEEPSEEK_API_KEY is required'),\n baseUrl: z.string().url().default('https://api.deepseek.com'),\n showCostInfo: z.boolean().default(true),\n requestTimeout: z.number().positive().default(60000),\n maxRetries: z.number().min(0).max(10).default(2),\n skipConnectionTest: z.boolean().default(false),\n maxMessageLength: z.number().positive().default(100_000),\n});\n\nexport type Config = z.infer;\n\nlet cachedConfig: Config | null = null;\n\n/**\n * Load configuration from environment variables.\n * Validates with Zod and caches the result.\n * Throws ConfigError if validation fails.\n */\nexport function loadConfig(): Config {\n const raw = {\n apiKey: process.env.DEEPSEEK_API_KEY || '',\n baseUrl: process.env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com',\n showCostInfo: process.env.SHOW_COST_INFO !== 'false',\n requestTimeout: process.env.REQUEST_TIMEOUT\n ? parseInt(process.env.REQUEST_TIMEOUT, 10)\n : 60000,\n maxRetries: process.env.MAX_RETRIES\n ? parseInt(process.env.MAX_RETRIES, 10)\n : 2,\n skipConnectionTest: process.env.SKIP_CONNECTION_TEST === 'true',\n maxMessageLength: process.env.MAX_MESSAGE_LENGTH\n ? parseInt(process.env.MAX_MESSAGE_LENGTH, 10)\n : 100_000,\n };\n\n const result = ConfigSchema.safeParse(raw);\n\n if (!result.success) {\n const issues = result.error.issues.map((issue) => ({\n path: issue.path.join('.'),\n message: issue.message,\n }));\n\n const hint = !raw.apiKey\n ? '\\nPlease set your DeepSeek API key:\\n export DEEPSEEK_API_KEY=\"your-api-key-here\"'\n : '';\n\n throw new ConfigError(\n `Configuration validation failed${hint}`,\n issues\n );\n }\n\n cachedConfig = result.data;\n return cachedConfig;\n}\n\n/**\n * Get the cached configuration.\n * Throws if loadConfig() hasn't been called yet.\n */\nexport function getConfig(): Config {\n if (!cachedConfig) {\n throw new Error('Config not loaded. Call loadConfig() first.');\n }\n return cachedConfig;\n}\n\n/**\n * Reset cached configuration (for testing).\n */\nexport function resetConfig(): void {\n cachedConfig = null;\n}\n"}, "a3f04a4ad5f56eed8d89f8f7a78c17d358745eb1": {"t": 1785749511.4565964, "n": 2039, "h": "fec6747093ed40da", "c": "/**\n * Centralized Configuration\n * Loads and validates configuration from environment variables\n */\n\nimport { z } from 'zod';\n\nconst ConfigSchema = z.object({\n apiKey: z.string().min(1, 'DEEPSEEK_API_KEY is required'),\n baseUrl: z.string().url().default('https://api.deepseek.com'),\n showCostInfo: z.boolean().default(true),\n requestTimeout: z.number().positive().default(60000),\n maxRetries: z.number().min(0).max(10).default(2),\n});\n\nexport type Config = z.infer;\n\nlet cachedConfig: Config | null = null;\n\n/**\n * Load configuration from environment variables.\n * Validates with Zod and caches the result.\n * Exits process if validation fails (e.g., missing API key).\n */\nexport function loadConfig(): Config {\n const raw = {\n apiKey: process.env.DEEPSEEK_API_KEY || '',\n baseUrl: process.env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com',\n showCostInfo: process.env.SHOW_COST_INFO !== 'false',\n requestTimeout: process.env.REQUEST_TIMEOUT\n ? parseInt(process.env.REQUEST_TIMEOUT, 10)\n : 60000,\n maxRetries: process.env.MAX_RETRIES\n ? parseInt(process.env.MAX_RETRIES, 10)\n : 2,\n };\n\n const result = ConfigSchema.safeParse(raw);\n\n if (!result.success) {\n console.error('Error: Configuration validation failed');\n const issues = result.error.issues;\n for (const issue of issues) {\n console.error(` - ${issue.path.join('.')}: ${issue.message}`);\n }\n if (!raw.apiKey) {\n console.error('Please set your DeepSeek API key:');\n console.error(' export DEEPSEEK_API_KEY=\"your-api-key-here\"');\n }\n process.exit(1);\n }\n\n cachedConfig = result.data;\n return cachedConfig;\n}\n\n/**\n * Get the cached configuration.\n * Throws if loadConfig() hasn't been called yet.\n */\nexport function getConfig(): Config {\n if (!cachedConfig) {\n throw new Error('Config not loaded. Call loadConfig() first.');\n }\n return cachedConfig;\n}\n\n/**\n * Reset cached configuration (for testing).\n */\nexport function resetConfig(): void {\n cachedConfig = null;\n}\n"}}, "src/index.ts": {"5eb18b60abee16172cb9d04e70b4c46fd470783a": {"t": 1785745561.2164335, "n": 4789, "h": "3a7f18e3d0cbe538", "c": "#!/usr/bin/env node\n\n/**\n * DeepSeek MCP Server\n * Model Context Protocol server for DeepSeek API integration\n */\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { loadConfig, getConfig } from './config.js';\nimport { ConfigError } from './errors.js';\nimport { DeepSeekClient } from './deepseek-client.js';\nimport { createServer, version } from './server.js';\nimport { registerAllTools } from './tools/index.js';\nimport { registerAllPrompts } from './prompts/index.js';\nimport { registerAllResources } from './resources/index.js';\nimport { startHttpTransport } from './transport-http.js';\nimport { SessionStore } from './session.js';\nimport { UsageTracker } from './usage-tracker.js';\n\nasync function main() {\n // Load and validate configuration\n try {\n loadConfig();\n } catch (error) {\n if (error instanceof ConfigError) {\n console.error('Error: ' + error.message);\n for (const issue of error.issues) {\n console.error(` - ${issue.path}: ${issue.message}`);\n }\n }\n process.exit(1);\n }\n\n const config = getConfig();\n const deepseek = new DeepSeekClient();\n const server = createServer();\n\n // STDIO mode: single shared SessionStore for the lifetime of this process.\n // (HTTP mode builds its own per-session stores below — see serverFactory.)\n const stdioSessionStore = new SessionStore();\n\n // Register tools, prompts, and resources\n registerAllTools(server, deepseek, stdioSessionStore);\n registerAllPrompts(server);\n registerAllResources(server);\n\n // Wire the usage tracker's active-session count to the STDIO store.\n // In HTTP mode this is intentionally NOT wired: each HTTP session owns its\n // own store, so a process-wide count would be misleading and could leak\n // tenant info.\n if (config.transport !== 'http') {\n UsageTracker.getInstance().setSessionSource(() => stdioSessionStore.list().length);\n }\n\n console.error(`[DeepSeek MCP] Starting server v${version}...`);\n\n // Optional connection test (controlled by SKIP_CONNECTION_TEST env)\n if (!config.skipConnectionTest) {\n console.error('[DeepSeek MCP] Testing API connection...');\n const isConnected = await deepseek.testConnection();\n\n if (!isConnected) {\n console.error('[DeepSeek MCP] Warning: Failed to connect to DeepSeek API');\n console.error(\n '[DeepSeek MCP] Please check your API key and internet connection'\n );\n } else {\n console.error('[DeepSeek MCP] API connection successful');\n }\n }\n\n if (config.transport === 'http') {\n // HTTP transport: per-session McpServer AND per-session SessionStore.\n // DeepSeekClient is shared (stateless API client is fine cross-session).\n // Each HTTP session gets an isolated SessionStore so that one client\n // cannot read, list, or clear another client's conversation history.\n const serverFactory = () => {\n const s = createServer();\n const sessionStore = new SessionStore();\n registerAllTools(s, deepseek, sessionStore);\n registerAllPrompts(s);\n registerAllResources(s);\n return s;\n };\n await startHttpTransport(serverFactory, config.httpPort, {\n host: config.httpHost,\n authToken: config.httpAuthToken,\n allowedHosts: config.httpAllowedHosts,\n });\n } else {\n // Stdio transport (default)\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error('[DeepSeek MCP] Server running on stdio');\n console.error(\n '[DeepSeek MCP] Available tools: deepseek_chat (sessions, fallback), deepseek_fim (fill-in-the-middle), deepseek_sessions'\n );\n console.error('[DeepSeek MCP] Available prompts: 12 reasoning templates');\n console.error('[DeepSeek MCP] Available resources: deepseek://models, deepseek://config, deepseek://usage');\n }\n}\n\n// Error handling\nprocess.on('uncaughtException', (error) => {\n console.error('[DeepSeek MCP] Uncaught exception:', error);\n process.exit(1);\n});\n\nprocess.on('unhandledRejection', (reason, promise) => {\n console.error(\n '[DeepSeek MCP] Unhandled rejection at:',\n promise,\n 'reason:',\n reason\n );\n process.exit(1);\n});\n\n// Smithery sandbox: allows scanning tools/resources without real credentials\nexport function createSandboxServer() {\n process.env.DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY || 'sandbox-key';\n process.env.SKIP_CONNECTION_TEST = 'true';\n loadConfig();\n const client = new DeepSeekClient();\n const server = createServer();\n const sessionStore = new SessionStore();\n registerAllTools(server, client, sessionStore);\n registerAllPrompts(server);\n registerAllResources(server);\n return server;\n}\n\n// Start the server\nmain().catch((error) => {\n console.error('[DeepSeek MCP] Fatal error:', error);\n process.exit(1);\n});\n"}, "f54b61e09e3baf2233e65c66c830ff43d4a87d46": {"t": 1785749503.0251322, "n": 4789, "h": "3a7f18e3d0cbe538", "c": "#!/usr/bin/env node\n\n/**\n * DeepSeek MCP Server\n * Model Context Protocol server for DeepSeek API integration\n */\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { loadConfig, getConfig } from './config.js';\nimport { ConfigError } from './errors.js';\nimport { DeepSeekClient } from './deepseek-client.js';\nimport { createServer, version } from './server.js';\nimport { registerAllTools } from './tools/index.js';\nimport { registerAllPrompts } from './prompts/index.js';\nimport { registerAllResources } from './resources/index.js';\nimport { startHttpTransport } from './transport-http.js';\nimport { SessionStore } from './session.js';\nimport { UsageTracker } from './usage-tracker.js';\n\nasync function main() {\n // Load and validate configuration\n try {\n loadConfig();\n } catch (error) {\n if (error instanceof ConfigError) {\n console.error('Error: ' + error.message);\n for (const issue of error.issues) {\n console.error(` - ${issue.path}: ${issue.message}`);\n }\n }\n process.exit(1);\n }\n\n const config = getConfig();\n const deepseek = new DeepSeekClient();\n const server = createServer();\n\n // STDIO mode: single shared SessionStore for the lifetime of this process.\n // (HTTP mode builds its own per-session stores below — see serverFactory.)\n const stdioSessionStore = new SessionStore();\n\n // Register tools, prompts, and resources\n registerAllTools(server, deepseek, stdioSessionStore);\n registerAllPrompts(server);\n registerAllResources(server);\n\n // Wire the usage tracker's active-session count to the STDIO store.\n // In HTTP mode this is intentionally NOT wired: each HTTP session owns its\n // own store, so a process-wide count would be misleading and could leak\n // tenant info.\n if (config.transport !== 'http') {\n UsageTracker.getInstance().setSessionSource(() => stdioSessionStore.list().length);\n }\n\n console.error(`[DeepSeek MCP] Starting server v${version}...`);\n\n // Optional connection test (controlled by SKIP_CONNECTION_TEST env)\n if (!config.skipConnectionTest) {\n console.error('[DeepSeek MCP] Testing API connection...');\n const isConnected = await deepseek.testConnection();\n\n if (!isConnected) {\n console.error('[DeepSeek MCP] Warning: Failed to connect to DeepSeek API');\n console.error(\n '[DeepSeek MCP] Please check your API key and internet connection'\n );\n } else {\n console.error('[DeepSeek MCP] API connection successful');\n }\n }\n\n if (config.transport === 'http') {\n // HTTP transport: per-session McpServer AND per-session SessionStore.\n // DeepSeekClient is shared (stateless API client is fine cross-session).\n // Each HTTP session gets an isolated SessionStore so that one client\n // cannot read, list, or clear another client's conversation history.\n const serverFactory = () => {\n const s = createServer();\n const sessionStore = new SessionStore();\n registerAllTools(s, deepseek, sessionStore);\n registerAllPrompts(s);\n registerAllResources(s);\n return s;\n };\n await startHttpTransport(serverFactory, config.httpPort, {\n host: config.httpHost,\n authToken: config.httpAuthToken,\n allowedHosts: config.httpAllowedHosts,\n });\n } else {\n // Stdio transport (default)\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error('[DeepSeek MCP] Server running on stdio');\n console.error(\n '[DeepSeek MCP] Available tools: deepseek_chat (sessions, fallback), deepseek_fim (fill-in-the-middle), deepseek_sessions'\n );\n console.error('[DeepSeek MCP] Available prompts: 12 reasoning templates');\n console.error('[DeepSeek MCP] Available resources: deepseek://models, deepseek://config, deepseek://usage');\n }\n}\n\n// Error handling\nprocess.on('uncaughtException', (error) => {\n console.error('[DeepSeek MCP] Uncaught exception:', error);\n process.exit(1);\n});\n\nprocess.on('unhandledRejection', (reason, promise) => {\n console.error(\n '[DeepSeek MCP] Unhandled rejection at:',\n promise,\n 'reason:',\n reason\n );\n process.exit(1);\n});\n\n// Smithery sandbox: allows scanning tools/resources without real credentials\nexport function createSandboxServer() {\n process.env.DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY || 'sandbox-key';\n process.env.SKIP_CONNECTION_TEST = 'true';\n loadConfig();\n const client = new DeepSeekClient();\n const server = createServer();\n const sessionStore = new SessionStore();\n registerAllTools(server, client, sessionStore);\n registerAllPrompts(server);\n registerAllResources(server);\n return server;\n}\n\n// Start the server\nmain().catch((error) => {\n console.error('[DeepSeek MCP] Fatal error:', error);\n process.exit(1);\n});\n"}, "dab07ed93ddde0ab219d4cb7066785847db53a32": {"t": 1785749505.1013567, "n": 4754, "h": "26972b473c11ad0e", "c": "#!/usr/bin/env node\n\n/**\n * DeepSeek MCP Server\n * Model Context Protocol server for DeepSeek API integration\n */\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { loadConfig, getConfig } from './config.js';\nimport { ConfigError } from './errors.js';\nimport { DeepSeekClient } from './deepseek-client.js';\nimport { createServer, version } from './server.js';\nimport { registerAllTools } from './tools/index.js';\nimport { registerAllPrompts } from './prompts/index.js';\nimport { registerAllResources } from './resources/index.js';\nimport { startHttpTransport } from './transport-http.js';\nimport { SessionStore } from './session.js';\nimport { UsageTracker } from './usage-tracker.js';\n\nasync function main() {\n // Load and validate configuration\n try {\n loadConfig();\n } catch (error) {\n if (error instanceof ConfigError) {\n console.error('Error: ' + error.message);\n for (const issue of error.issues) {\n console.error(` - ${issue.path}: ${issue.message}`);\n }\n }\n process.exit(1);\n }\n\n const config = getConfig();\n const deepseek = new DeepSeekClient();\n const server = createServer();\n\n // STDIO mode: single shared SessionStore for the lifetime of this process.\n // (HTTP mode builds its own per-session stores below — see serverFactory.)\n const stdioSessionStore = new SessionStore();\n\n // Register tools, prompts, and resources\n registerAllTools(server, deepseek, stdioSessionStore);\n registerAllPrompts(server);\n registerAllResources(server);\n\n // Wire the usage tracker's active-session count to the STDIO store.\n // In HTTP mode this is intentionally NOT wired: each HTTP session owns its\n // own store, so a process-wide count would be misleading and could leak\n // tenant info.\n if (config.transport !== 'http') {\n UsageTracker.getInstance().setSessionSource(() => stdioSessionStore.list().length);\n }\n\n console.error(`[DeepSeek MCP] Starting server v${version}...`);\n\n // Optional connection test (controlled by SKIP_CONNECTION_TEST env)\n if (!config.skipConnectionTest) {\n console.error('[DeepSeek MCP] Testing API connection...');\n const isConnected = await deepseek.testConnection();\n\n if (!isConnected) {\n console.error('[DeepSeek MCP] Warning: Failed to connect to DeepSeek API');\n console.error(\n '[DeepSeek MCP] Please check your API key and internet connection'\n );\n } else {\n console.error('[DeepSeek MCP] API connection successful');\n }\n }\n\n if (config.transport === 'http') {\n // HTTP transport: per-session McpServer AND per-session SessionStore.\n // DeepSeekClient is shared (stateless API client is fine cross-session).\n // Each HTTP session gets an isolated SessionStore so that one client\n // cannot read, list, or clear another client's conversation history.\n const serverFactory = () => {\n const s = createServer();\n const sessionStore = new SessionStore();\n registerAllTools(s, deepseek, sessionStore);\n registerAllPrompts(s);\n registerAllResources(s);\n return s;\n };\n await startHttpTransport(serverFactory, config.httpPort, {\n host: config.httpHost,\n authToken: config.httpAuthToken,\n allowedHosts: config.httpAllowedHosts,\n });\n } else {\n // Stdio transport (default)\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error('[DeepSeek MCP] Server running on stdio');\n console.error(\n '[DeepSeek MCP] Available tools: deepseek_chat (sessions, fallback), deepseek_sessions'\n );\n console.error('[DeepSeek MCP] Available prompts: 12 reasoning templates');\n console.error('[DeepSeek MCP] Available resources: deepseek://models, deepseek://config, deepseek://usage');\n }\n}\n\n// Error handling\nprocess.on('uncaughtException', (error) => {\n console.error('[DeepSeek MCP] Uncaught exception:', error);\n process.exit(1);\n});\n\nprocess.on('unhandledRejection', (reason, promise) => {\n console.error(\n '[DeepSeek MCP] Unhandled rejection at:',\n promise,\n 'reason:',\n reason\n );\n process.exit(1);\n});\n\n// Smithery sandbox: allows scanning tools/resources without real credentials\nexport function createSandboxServer() {\n process.env.DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY || 'sandbox-key';\n process.env.SKIP_CONNECTION_TEST = 'true';\n loadConfig();\n const client = new DeepSeekClient();\n const server = createServer();\n const sessionStore = new SessionStore();\n registerAllTools(server, client, sessionStore);\n registerAllPrompts(server);\n registerAllResources(server);\n return server;\n}\n\n// Start the server\nmain().catch((error) => {\n console.error('[DeepSeek MCP] Fatal error:', error);\n process.exit(1);\n});\n"}, "9fd514292d23d59ca1434b01f019aca6ef4356f9": {"t": 1785749506.1722593, "n": 4632, "h": "5fe5147379c7fd6e", "c": "#!/usr/bin/env node\n\n/**\n * DeepSeek MCP Server\n * Model Context Protocol server for DeepSeek API integration\n */\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { loadConfig, getConfig } from './config.js';\nimport { ConfigError } from './errors.js';\nimport { DeepSeekClient } from './deepseek-client.js';\nimport { createServer, version } from './server.js';\nimport { registerAllTools } from './tools/index.js';\nimport { registerAllPrompts } from './prompts/index.js';\nimport { registerAllResources } from './resources/index.js';\nimport { startHttpTransport } from './transport-http.js';\nimport { SessionStore } from './session.js';\nimport { UsageTracker } from './usage-tracker.js';\n\nasync function main() {\n // Load and validate configuration\n try {\n loadConfig();\n } catch (error) {\n if (error instanceof ConfigError) {\n console.error('Error: ' + error.message);\n for (const issue of error.issues) {\n console.error(` - ${issue.path}: ${issue.message}`);\n }\n }\n process.exit(1);\n }\n\n const config = getConfig();\n const deepseek = new DeepSeekClient();\n const server = createServer();\n\n // STDIO mode: single shared SessionStore for the lifetime of this process.\n // (HTTP mode builds its own per-session stores below — see serverFactory.)\n const stdioSessionStore = new SessionStore();\n\n // Register tools, prompts, and resources\n registerAllTools(server, deepseek, stdioSessionStore);\n registerAllPrompts(server);\n registerAllResources(server);\n\n // Wire the usage tracker's active-session count to the STDIO store.\n // In HTTP mode this is intentionally NOT wired: each HTTP session owns its\n // own store, so a process-wide count would be misleading and could leak\n // tenant info.\n if (config.transport !== 'http') {\n UsageTracker.getInstance().setSessionSource(() => stdioSessionStore.list().length);\n }\n\n console.error(`[DeepSeek MCP] Starting server v${version}...`);\n\n // Optional connection test (controlled by SKIP_CONNECTION_TEST env)\n if (!config.skipConnectionTest) {\n console.error('[DeepSeek MCP] Testing API connection...');\n const isConnected = await deepseek.testConnection();\n\n if (!isConnected) {\n console.error('[DeepSeek MCP] Warning: Failed to connect to DeepSeek API');\n console.error(\n '[DeepSeek MCP] Please check your API key and internet connection'\n );\n } else {\n console.error('[DeepSeek MCP] API connection successful');\n }\n }\n\n if (config.transport === 'http') {\n // HTTP transport: per-session McpServer AND per-session SessionStore.\n // DeepSeekClient is shared (stateless API client is fine cross-session).\n // Each HTTP session gets an isolated SessionStore so that one client\n // cannot read, list, or clear another client's conversation history.\n const serverFactory = () => {\n const s = createServer();\n const sessionStore = new SessionStore();\n registerAllTools(s, deepseek, sessionStore);\n registerAllPrompts(s);\n registerAllResources(s);\n return s;\n };\n await startHttpTransport(serverFactory, config.httpPort);\n } else {\n // Stdio transport (default)\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error('[DeepSeek MCP] Server running on stdio');\n console.error(\n '[DeepSeek MCP] Available tools: deepseek_chat (sessions, fallback), deepseek_sessions'\n );\n console.error('[DeepSeek MCP] Available prompts: 12 reasoning templates');\n console.error('[DeepSeek MCP] Available resources: deepseek://models, deepseek://config, deepseek://usage');\n }\n}\n\n// Error handling\nprocess.on('uncaughtException', (error) => {\n console.error('[DeepSeek MCP] Uncaught exception:', error);\n process.exit(1);\n});\n\nprocess.on('unhandledRejection', (reason, promise) => {\n console.error(\n '[DeepSeek MCP] Unhandled rejection at:',\n promise,\n 'reason:',\n reason\n );\n process.exit(1);\n});\n\n// Smithery sandbox: allows scanning tools/resources without real credentials\nexport function createSandboxServer() {\n process.env.DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY || 'sandbox-key';\n process.env.SKIP_CONNECTION_TEST = 'true';\n loadConfig();\n const client = new DeepSeekClient();\n const server = createServer();\n const sessionStore = new SessionStore();\n registerAllTools(server, client, sessionStore);\n registerAllPrompts(server);\n registerAllResources(server);\n return server;\n}\n\n// Start the server\nmain().catch((error) => {\n console.error('[DeepSeek MCP] Fatal error:', error);\n process.exit(1);\n});\n"}, "9e364641ed058d311518ae2261fc5c5d95e34c53": {"t": 1785749507.2487395, "n": 3589, "h": "4c9b584cdce354f5", "c": "#!/usr/bin/env node\n\n/**\n * DeepSeek MCP Server\n * Model Context Protocol server for DeepSeek API integration\n */\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { loadConfig, getConfig } from './config.js';\nimport { ConfigError } from './errors.js';\nimport { DeepSeekClient } from './deepseek-client.js';\nimport { createServer, version } from './server.js';\nimport { registerAllTools } from './tools/index.js';\nimport { registerAllPrompts } from './prompts/index.js';\nimport { registerAllResources } from './resources/index.js';\nimport { startHttpTransport } from './transport-http.js';\n\nasync function main() {\n // Load and validate configuration\n try {\n loadConfig();\n } catch (error) {\n if (error instanceof ConfigError) {\n console.error('Error: ' + error.message);\n for (const issue of error.issues) {\n console.error(` - ${issue.path}: ${issue.message}`);\n }\n }\n process.exit(1);\n }\n\n const config = getConfig();\n const deepseek = new DeepSeekClient();\n const server = createServer();\n\n // Register tools, prompts, and resources\n registerAllTools(server, deepseek);\n registerAllPrompts(server);\n registerAllResources(server);\n\n console.error(`[DeepSeek MCP] Starting server v${version}...`);\n\n // Optional connection test (controlled by SKIP_CONNECTION_TEST env)\n if (!config.skipConnectionTest) {\n console.error('[DeepSeek MCP] Testing API connection...');\n const isConnected = await deepseek.testConnection();\n\n if (!isConnected) {\n console.error('[DeepSeek MCP] Warning: Failed to connect to DeepSeek API');\n console.error(\n '[DeepSeek MCP] Please check your API key and internet connection'\n );\n } else {\n console.error('[DeepSeek MCP] API connection successful');\n }\n }\n\n if (config.transport === 'http') {\n // HTTP transport: per-session McpServer, shared DeepSeekClient\n const serverFactory = () => {\n const s = createServer();\n registerAllTools(s, deepseek);\n registerAllPrompts(s);\n registerAllResources(s);\n return s;\n };\n await startHttpTransport(serverFactory, config.httpPort);\n } else {\n // Stdio transport (default)\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error('[DeepSeek MCP] Server running on stdio');\n console.error(\n '[DeepSeek MCP] Available tools: deepseek_chat (sessions, fallback), deepseek_sessions'\n );\n console.error('[DeepSeek MCP] Available prompts: 12 reasoning templates');\n console.error('[DeepSeek MCP] Available resources: deepseek://models, deepseek://config, deepseek://usage');\n }\n}\n\n// Error handling\nprocess.on('uncaughtException', (error) => {\n console.error('[DeepSeek MCP] Uncaught exception:', error);\n process.exit(1);\n});\n\nprocess.on('unhandledRejection', (reason, promise) => {\n console.error(\n '[DeepSeek MCP] Unhandled rejection at:',\n promise,\n 'reason:',\n reason\n );\n process.exit(1);\n});\n\n// Smithery sandbox: allows scanning tools/resources without real credentials\nexport function createSandboxServer() {\n process.env.DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY || 'sandbox-key';\n process.env.SKIP_CONNECTION_TEST = 'true';\n loadConfig();\n const client = new DeepSeekClient();\n const server = createServer();\n registerAllTools(server, client);\n registerAllPrompts(server);\n registerAllResources(server);\n return server;\n}\n\n// Start the server\nmain().catch((error) => {\n console.error('[DeepSeek MCP] Fatal error:', error);\n process.exit(1);\n});\n"}, "f630c579db017410837f962696f112eb168eb586": {"t": 1785749508.3033092, "n": 3146, "h": "004e6c0c56f8df25", "c": "#!/usr/bin/env node\n\n/**\n * DeepSeek MCP Server\n * Model Context Protocol server for DeepSeek API integration\n */\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { loadConfig, getConfig } from './config.js';\nimport { ConfigError } from './errors.js';\nimport { DeepSeekClient } from './deepseek-client.js';\nimport { createServer, version } from './server.js';\nimport { registerAllTools } from './tools/index.js';\nimport { registerAllPrompts } from './prompts/index.js';\nimport { registerAllResources } from './resources/index.js';\n\nasync function main() {\n // Load and validate configuration\n try {\n loadConfig();\n } catch (error) {\n if (error instanceof ConfigError) {\n console.error('Error: ' + error.message);\n for (const issue of error.issues) {\n console.error(` - ${issue.path}: ${issue.message}`);\n }\n }\n process.exit(1);\n }\n\n const config = getConfig();\n const deepseek = new DeepSeekClient();\n const server = createServer();\n\n // Register tools, prompts, and resources\n registerAllTools(server, deepseek);\n registerAllPrompts(server);\n registerAllResources(server);\n\n console.error(`[DeepSeek MCP] Starting server v${version}...`);\n\n // Optional connection test (controlled by SKIP_CONNECTION_TEST env)\n if (!config.skipConnectionTest) {\n console.error('[DeepSeek MCP] Testing API connection...');\n const isConnected = await deepseek.testConnection();\n\n if (!isConnected) {\n console.error('[DeepSeek MCP] Warning: Failed to connect to DeepSeek API');\n console.error(\n '[DeepSeek MCP] Please check your API key and internet connection'\n );\n } else {\n console.error('[DeepSeek MCP] API connection successful');\n }\n }\n\n // Connect to stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error('[DeepSeek MCP] Server running on stdio');\n console.error(\n '[DeepSeek MCP] Available tools: deepseek_chat (sessions, fallback), deepseek_sessions'\n );\n console.error('[DeepSeek MCP] Available prompts: 12 reasoning templates');\n console.error('[DeepSeek MCP] Available resources: deepseek://models, deepseek://config, deepseek://usage');\n}\n\n// Error handling\nprocess.on('uncaughtException', (error) => {\n console.error('[DeepSeek MCP] Uncaught exception:', error);\n process.exit(1);\n});\n\nprocess.on('unhandledRejection', (reason, promise) => {\n console.error(\n '[DeepSeek MCP] Unhandled rejection at:',\n promise,\n 'reason:',\n reason\n );\n process.exit(1);\n});\n\n// Smithery sandbox: allows scanning tools/resources without real credentials\nexport function createSandboxServer() {\n process.env.DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY || 'sandbox-key';\n process.env.SKIP_CONNECTION_TEST = 'true';\n loadConfig();\n const client = new DeepSeekClient();\n const server = createServer();\n registerAllTools(server, client);\n registerAllPrompts(server);\n registerAllResources(server);\n return server;\n}\n\n// Start the server\nmain().catch((error) => {\n console.error('[DeepSeek MCP] Fatal error:', error);\n process.exit(1);\n});\n"}, "d9a2fab03326ab6f722658de2c8b7461433f2ea9": {"t": 1785749509.4141273, "n": 2697, "h": "788efd897976fbd7", "c": "#!/usr/bin/env node\n\n/**\n * DeepSeek MCP Server\n * Model Context Protocol server for DeepSeek API integration\n */\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { loadConfig, getConfig } from './config.js';\nimport { ConfigError } from './errors.js';\nimport { DeepSeekClient } from './deepseek-client.js';\nimport { createServer, version } from './server.js';\nimport { registerAllTools } from './tools/index.js';\nimport { registerAllPrompts } from './prompts/index.js';\nimport { registerAllResources } from './resources/index.js';\n\nasync function main() {\n // Load and validate configuration\n try {\n loadConfig();\n } catch (error) {\n if (error instanceof ConfigError) {\n console.error('Error: ' + error.message);\n for (const issue of error.issues) {\n console.error(` - ${issue.path}: ${issue.message}`);\n }\n }\n process.exit(1);\n }\n\n const config = getConfig();\n const deepseek = new DeepSeekClient();\n const server = createServer();\n\n // Register tools, prompts, and resources\n registerAllTools(server, deepseek);\n registerAllPrompts(server);\n registerAllResources(server);\n\n console.error(`[DeepSeek MCP] Starting server v${version}...`);\n\n // Optional connection test (controlled by SKIP_CONNECTION_TEST env)\n if (!config.skipConnectionTest) {\n console.error('[DeepSeek MCP] Testing API connection...');\n const isConnected = await deepseek.testConnection();\n\n if (!isConnected) {\n console.error('[DeepSeek MCP] Warning: Failed to connect to DeepSeek API');\n console.error(\n '[DeepSeek MCP] Please check your API key and internet connection'\n );\n } else {\n console.error('[DeepSeek MCP] API connection successful');\n }\n }\n\n // Connect to stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error('[DeepSeek MCP] Server running on stdio');\n console.error(\n '[DeepSeek MCP] Available tools: deepseek_chat (sessions, fallback), deepseek_sessions'\n );\n console.error('[DeepSeek MCP] Available prompts: 12 reasoning templates');\n console.error('[DeepSeek MCP] Available resources: deepseek://models, deepseek://config, deepseek://usage');\n}\n\n// Error handling\nprocess.on('uncaughtException', (error) => {\n console.error('[DeepSeek MCP] Uncaught exception:', error);\n process.exit(1);\n});\n\nprocess.on('unhandledRejection', (reason, promise) => {\n console.error(\n '[DeepSeek MCP] Unhandled rejection at:',\n promise,\n 'reason:',\n reason\n );\n process.exit(1);\n});\n\n// Start the server\nmain().catch((error) => {\n console.error('[DeepSeek MCP] Fatal error:', error);\n process.exit(1);\n});\n"}, "d08fbeadacd6deb176fba497c16e216536124a8d": {"t": 1785749510.4844277, "n": 2687, "h": "a9515df00e06418c", "c": "#!/usr/bin/env node\n\n/**\n * DeepSeek MCP Server\n * Model Context Protocol server for DeepSeek API integration\n */\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { loadConfig } from './config.js';\nimport { ConfigError } from './errors.js';\nimport { DeepSeekClient } from './deepseek-client.js';\nimport { createServer, version } from './server.js';\nimport { registerAllTools } from './tools/index.js';\nimport { registerAllPrompts } from './prompts/index.js';\nimport { registerAllResources } from './resources/index.js';\n\nasync function main() {\n // Load and validate configuration\n try {\n loadConfig();\n } catch (error) {\n if (error instanceof ConfigError) {\n console.error('Error: ' + error.message);\n for (const issue of error.issues) {\n console.error(` - ${issue.path}: ${issue.message}`);\n }\n }\n process.exit(1);\n }\n\n const config = loadConfig();\n const deepseek = new DeepSeekClient();\n const server = createServer();\n\n // Register tools, prompts, and resources\n registerAllTools(server, deepseek);\n registerAllPrompts(server);\n registerAllResources(server);\n\n console.error(`[DeepSeek MCP] Starting server v${version}...`);\n\n // Optional connection test (controlled by SKIP_CONNECTION_TEST env)\n if (!config.skipConnectionTest) {\n console.error('[DeepSeek MCP] Testing API connection...');\n const isConnected = await deepseek.testConnection();\n\n if (!isConnected) {\n console.error('[DeepSeek MCP] Warning: Failed to connect to DeepSeek API');\n console.error(\n '[DeepSeek MCP] Please check your API key and internet connection'\n );\n } else {\n console.error('[DeepSeek MCP] API connection successful');\n }\n }\n\n // Connect to stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error('[DeepSeek MCP] Server running on stdio');\n console.error(\n '[DeepSeek MCP] Available tools: deepseek_chat (sessions, fallback), deepseek_sessions'\n );\n console.error('[DeepSeek MCP] Available prompts: 12 reasoning templates');\n console.error('[DeepSeek MCP] Available resources: deepseek://models, deepseek://config, deepseek://usage');\n}\n\n// Error handling\nprocess.on('uncaughtException', (error) => {\n console.error('[DeepSeek MCP] Uncaught exception:', error);\n process.exit(1);\n});\n\nprocess.on('unhandledRejection', (reason, promise) => {\n console.error(\n '[DeepSeek MCP] Unhandled rejection at:',\n promise,\n 'reason:',\n reason\n );\n process.exit(1);\n});\n\n// Start the server\nmain().catch((error) => {\n console.error('[DeepSeek MCP] Fatal error:', error);\n process.exit(1);\n});\n"}, "440b7099f12fcab015d490983725d13e305ddc76": {"t": 1785749511.5672429, "n": 2463, "h": "938251827a2d6019", "c": "#!/usr/bin/env node\n\n/**\n * DeepSeek MCP Server\n * Model Context Protocol server for DeepSeek API integration\n */\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { loadConfig } from './config.js';\nimport { ConfigError } from './errors.js';\nimport { DeepSeekClient } from './deepseek-client.js';\nimport { createServer, version } from './server.js';\nimport { registerAllTools } from './tools/index.js';\nimport { registerAllPrompts } from './prompts/index.js';\n\nasync function main() {\n // Load and validate configuration\n try {\n loadConfig();\n } catch (error) {\n if (error instanceof ConfigError) {\n console.error('Error: ' + error.message);\n for (const issue of error.issues) {\n console.error(` - ${issue.path}: ${issue.message}`);\n }\n }\n process.exit(1);\n }\n\n const config = loadConfig();\n const deepseek = new DeepSeekClient();\n const server = createServer();\n\n // Register tools and prompts\n registerAllTools(server, deepseek);\n registerAllPrompts(server);\n\n console.error(`[DeepSeek MCP] Starting server v${version}...`);\n\n // Optional connection test (controlled by SKIP_CONNECTION_TEST env)\n if (!config.skipConnectionTest) {\n console.error('[DeepSeek MCP] Testing API connection...');\n const isConnected = await deepseek.testConnection();\n\n if (!isConnected) {\n console.error('[DeepSeek MCP] Warning: Failed to connect to DeepSeek API');\n console.error(\n '[DeepSeek MCP] Please check your API key and internet connection'\n );\n } else {\n console.error('[DeepSeek MCP] API connection successful');\n }\n }\n\n // Connect to stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error('[DeepSeek MCP] Server running on stdio');\n console.error(\n '[DeepSeek MCP] Available tools: deepseek_chat (with function calling support)'\n );\n console.error('[DeepSeek MCP] Available prompts: 12 reasoning templates');\n}\n\n// Error handling\nprocess.on('uncaughtException', (error) => {\n console.error('[DeepSeek MCP] Uncaught exception:', error);\n process.exit(1);\n});\n\nprocess.on('unhandledRejection', (reason, promise) => {\n console.error(\n '[DeepSeek MCP] Unhandled rejection at:',\n promise,\n 'reason:',\n reason\n );\n process.exit(1);\n});\n\n// Start the server\nmain().catch((error) => {\n console.error('[DeepSeek MCP] Fatal error:', error);\n process.exit(1);\n});\n"}, "a3f04a4ad5f56eed8d89f8f7a78c17d358745eb1": {"t": 1785749512.9309177, "n": 22863, "h": "77d22077b66dd80f", "c": "#!/usr/bin/env node\n\n/**\n * DeepSeek MCP Server\n * Model Context Protocol server for DeepSeek API integration\n *\n * This server exposes DeepSeek's chat and reasoning models as MCP tools\n * that can be used by Claude Code and other MCP clients.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod';\nimport { loadConfig, getConfig } from './config.js';\nimport { calculateCost, formatCost } from './cost.js';\nimport {\n ExtendedMessageSchema,\n ChatInputWithToolsSchema,\n ToolDefinitionSchema,\n ToolChoiceSchema,\n} from './schemas.js';\nimport { DeepSeekClient } from './deepseek-client.js';\nimport type { DeepSeekChatInput } from './types.js';\n\n// Load and validate configuration\nconst config = loadConfig();\n\n// Initialize DeepSeek client (uses getConfig() internally)\nconst deepseek = new DeepSeekClient();\n\n// Create MCP server\nconst server = new McpServer({\n name: 'deepseek-mcp-server',\n version: '1.1.0',\n});\n\n/**\n * Tool: deepseek_chat\n *\n * Chat completion with DeepSeek models.\n * Supports both deepseek-chat and deepseek-reasoner (R1) models.\n * Supports function calling via the tools parameter.\n */\nserver.registerTool(\n 'deepseek_chat',\n {\n title: 'DeepSeek Chat Completion',\n description:\n 'Chat with DeepSeek AI models. Supports deepseek-chat for general conversations and ' +\n 'deepseek-reasoner (R1) for complex reasoning tasks with chain-of-thought explanations. ' +\n 'Supports function calling via the tools parameter for structured tool use. ' +\n 'The reasoner model provides both reasoning_content (thinking process) and content (final answer).',\n inputSchema: {\n messages: z\n .array(ExtendedMessageSchema)\n .min(1)\n .describe(\n 'Array of conversation messages. Each message has role (system/user/assistant/tool) and content. Tool messages require tool_call_id.'\n ),\n model: z\n .enum(['deepseek-chat', 'deepseek-reasoner'])\n .default('deepseek-chat')\n .describe(\n 'Model to use. deepseek-chat for general tasks, deepseek-reasoner for complex reasoning'\n ),\n temperature: z\n .number()\n .min(0)\n .max(2)\n .optional()\n .describe('Sampling temperature (0-2). Higher = more random. Default: 1.0'),\n max_tokens: z\n .number()\n .min(1)\n .max(32768)\n .optional()\n .describe('Maximum tokens to generate. Default: model maximum'),\n stream: z\n .boolean()\n .optional()\n .default(false)\n .describe(\n 'Enable streaming mode. Returns full response after streaming completes.'\n ),\n tools: z\n .array(ToolDefinitionSchema)\n .max(128)\n .optional()\n .describe(\n 'Array of tool definitions for function calling. Each tool has type \"function\" and a function object with name, description, and parameters (JSON Schema).'\n ),\n tool_choice: ToolChoiceSchema.optional().describe(\n 'Controls which tool the model calls. \"auto\" (default), \"none\", \"required\", or {type:\"function\",function:{name:\"...\"}}'\n ),\n },\n outputSchema: {\n content: z.string(),\n reasoning_content: z.string().optional(),\n model: z.string(),\n usage: z.object({\n prompt_tokens: z.number(),\n completion_tokens: z.number(),\n total_tokens: z.number(),\n }),\n finish_reason: z.string(),\n tool_calls: z\n .array(\n z.object({\n id: z.string(),\n type: z.literal('function'),\n function: z.object({\n name: z.string(),\n arguments: z.string(),\n }),\n })\n )\n .optional(),\n },\n },\n async (input: DeepSeekChatInput) => {\n try {\n // Validate input with extended schema (supports tools)\n const validated = ChatInputWithToolsSchema.parse(input);\n\n console.error(\n `[DeepSeek MCP] Request: model=${validated.model}, messages=${validated.messages.length}, stream=${validated.stream}${validated.tools ? `, tools=${validated.tools.length}` : ''}`\n );\n\n // Call appropriate method based on stream parameter\n const response = validated.stream\n ? await deepseek.createStreamingChatCompletion({\n model: validated.model,\n messages: validated.messages,\n temperature: validated.temperature,\n max_tokens: validated.max_tokens,\n tools: validated.tools,\n tool_choice: validated.tool_choice,\n })\n : await deepseek.createChatCompletion({\n model: validated.model,\n messages: validated.messages,\n temperature: validated.temperature,\n max_tokens: validated.max_tokens,\n tools: validated.tools,\n tool_choice: validated.tool_choice,\n });\n\n console.error(\n `[DeepSeek MCP] Response: tokens=${response.usage.total_tokens}, finish_reason=${response.finish_reason}${response.tool_calls ? `, tool_calls=${response.tool_calls.length}` : ''}`\n );\n\n // Format response\n let responseText = '';\n\n // Add reasoning content if available (for deepseek-reasoner)\n if (response.reasoning_content) {\n responseText += `\\n${response.reasoning_content}\\n\\n\\n`;\n }\n\n responseText += response.content;\n\n // Format tool calls if present\n if (response.tool_calls?.length) {\n responseText += '\\n\\n**Function Calls:**\\n';\n for (const tc of response.tool_calls) {\n responseText += `\\`${tc.function.name}\\`\\n`;\n responseText += `- Call ID: ${tc.id}\\n`;\n responseText += `- Arguments: ${tc.function.arguments}\\n\\n`;\n }\n }\n\n // Calculate cost\n const cost = calculateCost(\n response.usage.prompt_tokens,\n response.usage.completion_tokens,\n response.model\n );\n\n // Add usage stats with cost information (controlled by config)\n if (getConfig().showCostInfo) {\n responseText += `\\n---\\n**Request Information:**\\n`;\n responseText += `- **Tokens:** ${response.usage.total_tokens} (${response.usage.prompt_tokens} prompt + ${response.usage.completion_tokens} completion)\\n`;\n responseText += `- **Model:** ${response.model}\\n`;\n responseText += `- **Cost:** ${formatCost(cost)}`;\n if (response.tool_calls?.length) {\n responseText += `\\n- **Tool Calls:** ${response.tool_calls.length}`;\n }\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: responseText,\n },\n ],\n structuredContent: {\n ...response,\n cost_usd: parseFloat(cost.toFixed(6)),\n } as unknown as Record,\n };\n } catch (error: any) {\n console.error('[DeepSeek MCP] Error:', error);\n const errorMessage = error?.message || 'Unknown error occurred';\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n }\n);\n\n/**\n * Register MCP Prompts\n * Pre-built prompt templates for common reasoning tasks\n */\n\n// Core Reasoning Prompts\nserver.registerPrompt(\n 'debug_with_reasoning',\n {\n title: 'Debug Code with Reasoning',\n description:\n 'Debug code issues using DeepSeek R1 reasoning model with step-by-step analysis',\n argsSchema: {\n code: z.string().describe('Code to debug'),\n error: z\n .string()\n .optional()\n .describe('Error message or description of the issue'),\n language: z.string().optional().describe('Programming language'),\n },\n },\n ({ code, error, language }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are an expert debugging assistant. Analyze this code using deep reasoning.\n\n${language ? `Language: ${language}` : ''}\n${error ? `Error/Issue: ${error}` : ''}\n\nCode:\n\\`\\`\\`\n${code}\n\\`\\`\\`\n\nPlease:\n1. Identify the bug or issue\n2. Explain your reasoning process step-by-step\n3. Suggest a fix with explanation\n4. Provide the corrected code\n\nUse the deepseek_chat tool with model: \"deepseek-reasoner\" for detailed reasoning.`,\n },\n },\n ],\n })\n);\n\nserver.registerPrompt(\n 'code_review_deep',\n {\n title: 'Deep Code Review',\n description:\n 'Comprehensive code review analyzing quality, security, performance, and best practices',\n argsSchema: {\n code: z.string().describe('Code to review'),\n language: z.string().optional().describe('Programming language'),\n focus: z\n .enum(['security', 'performance', 'quality', 'all'])\n .default('all')\n .describe('Review focus area'),\n },\n },\n ({ code, language, focus }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are an expert code reviewer. Perform a comprehensive code review.\n\n${language ? `Language: ${language}` : ''}\nFocus: ${focus === 'all' ? 'Security, Performance, Code Quality, Best Practices' : focus}\n\nCode:\n\\`\\`\\`\n${code}\n\\`\\`\\`\n\nFor each issue found, provide:\n1. **Issue**: What's wrong\n2. **Reasoning**: Why it's a problem\n3. **Severity**: Critical/High/Medium/Low\n4. **Fix**: How to resolve it\n\nUse the deepseek_chat tool with model: \"deepseek-reasoner\" for thorough analysis.`,\n },\n },\n ],\n })\n);\n\nserver.registerPrompt(\n 'research_synthesis',\n {\n title: 'Research & Synthesis',\n description:\n 'Research a topic and synthesize information into a structured report',\n argsSchema: {\n topic: z.string().describe('Topic to research'),\n context: z\n .string()\n .optional()\n .describe('Additional context or specific questions'),\n depth: z\n .enum(['brief', 'moderate', 'comprehensive'])\n .default('moderate')\n .describe('Research depth'),\n },\n },\n ({ topic, context, depth }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are a research assistant. Research and synthesize information about this topic.\n\nTopic: ${topic}\n${context ? `Context: ${context}` : ''}\nDepth: ${depth}\n\nPlease provide:\n1. **Overview**: Brief summary\n2. **Key Findings**: Main points with reasoning\n3. **Analysis**: Deep dive with reasoning process\n4. **Conclusion**: Synthesis and implications\n5. **Sources**: Cite reasoning steps\n\nUse the deepseek_chat tool with model: \"deepseek-reasoner\" for comprehensive analysis.`,\n },\n },\n ],\n })\n);\n\nserver.registerPrompt(\n 'strategic_planning',\n {\n title: 'Strategic Planning',\n description:\n 'Analyze options and create strategic plans with reasoning for each decision',\n argsSchema: {\n goal: z.string().describe('Goal or objective'),\n context: z.string().optional().describe('Situational context'),\n constraints: z.string().optional().describe('Constraints or limitations'),\n },\n },\n ({ goal, context, constraints }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are a strategic planning expert. Create a detailed plan with reasoning.\n\nGoal: ${goal}\n${context ? `Context: ${context}` : ''}\n${constraints ? `Constraints: ${constraints}` : ''}\n\nPlease provide:\n1. **Situation Analysis**: Current state with reasoning\n2. **Options**: List possible approaches\n3. **Evaluation**: Pros/cons of each option with reasoning\n4. **Recommendation**: Best approach with detailed reasoning\n5. **Action Plan**: Step-by-step plan with rationale\n\nUse the deepseek_chat tool with model: \"deepseek-reasoner\" for thorough strategic thinking.`,\n },\n },\n ],\n })\n);\n\nserver.registerPrompt(\n 'explain_like_im_five',\n {\n title: \"Explain Like I'm Five\",\n description:\n 'Explain complex topics in simple terms using analogies and reasoning',\n argsSchema: {\n topic: z.string().describe('Complex topic to explain'),\n audience: z\n .enum(['child', 'beginner', 'intermediate'])\n .default('beginner')\n .describe('Target audience level'),\n },\n },\n ({ topic, audience }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are an expert explainer. Make complex topics simple and understandable.\n\nTopic: ${topic}\nAudience: ${audience}\n\nPlease:\n1. Start with a simple analogy or metaphor\n2. Break down the concept step-by-step\n3. Use everyday examples\n4. Show your reasoning for why this explanation works\n5. Build up complexity gradually if needed\n\nUse the deepseek_chat tool with model: \"deepseek-reasoner\" to ensure logical explanations.`,\n },\n },\n ],\n })\n);\n\n// Advanced Prompts\nserver.registerPrompt(\n 'mathematical_proof',\n {\n title: 'Mathematical Proof',\n description:\n 'Prove mathematical statements with rigorous step-by-step reasoning',\n argsSchema: {\n statement: z.string().describe('Mathematical statement to prove'),\n context: z.string().optional().describe('Mathematical context or axioms'),\n },\n },\n ({ statement, context }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are a mathematician. Provide a rigorous proof.\n\nStatement to prove: ${statement}\n${context ? `Context/Axioms: ${context}` : ''}\n\nProvide:\n1. **Given**: What we know\n2. **To Prove**: What we're proving\n3. **Proof**: Step-by-step logical reasoning\n4. **Conclusion**: QED statement\n\nUse the deepseek_chat tool with model: \"deepseek-reasoner\" for strict logical reasoning.`,\n },\n },\n ],\n })\n);\n\nserver.registerPrompt(\n 'argument_validation',\n {\n title: 'Argument Validation',\n description:\n 'Analyze arguments for logical fallacies and reasoning errors',\n argsSchema: {\n argument: z.string().describe('Argument to validate'),\n type: z\n .enum(['informal', 'formal', 'both'])\n .default('informal')\n .describe('Analysis type'),\n },\n },\n ({ argument, type }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are a logic expert. Analyze this argument for validity.\n\nArgument:\n${argument}\n\nAnalysis type: ${type}\n\nPlease identify:\n1. **Structure**: Break down the argument's structure\n2. **Premises**: List all premises and assumptions\n3. **Conclusion**: What's being claimed\n4. **Reasoning**: Analyze the logical flow\n5. **Fallacies**: Any logical fallacies or errors\n6. **Validity**: Is the reasoning sound?\n7. **Improvements**: How to strengthen the argument\n\nUse the deepseek_chat tool with model: \"deepseek-reasoner\" for thorough logical analysis.`,\n },\n },\n ],\n })\n);\n\nserver.registerPrompt(\n 'creative_ideation',\n {\n title: 'Creative Ideation',\n description:\n 'Generate creative ideas with reasoning for feasibility and value',\n argsSchema: {\n challenge: z.string().describe('Problem or challenge to solve'),\n constraints: z\n .string()\n .optional()\n .describe('Constraints or requirements'),\n quantity: z\n .number()\n .min(1)\n .max(20)\n .default(5)\n .describe('Number of ideas to generate'),\n },\n },\n ({ challenge, constraints, quantity }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are a creative problem solver. Generate innovative ideas with reasoning.\n\nChallenge: ${challenge}\n${constraints ? `Constraints: ${constraints}` : ''}\nIdeas needed: ${quantity}\n\nFor each idea, provide:\n1. **Idea**: The concept\n2. **Reasoning**: Why this could work\n3. **Feasibility**: How realistic it is (High/Medium/Low)\n4. **Value**: Potential impact\n5. **Next Steps**: How to validate/implement\n\nUse the deepseek_chat tool with model: \"deepseek-reasoner\" for reasoned creativity.`,\n },\n },\n ],\n })\n);\n\nserver.registerPrompt(\n 'cost_comparison',\n {\n title: 'LLM Cost Comparison',\n description:\n 'Compare costs of different LLMs for a task and show savings with DeepSeek',\n argsSchema: {\n task: z.string().describe('Task description'),\n estimated_tokens: z\n .number()\n .min(100)\n .describe('Estimated token count (prompt + completion)'),\n },\n },\n ({ task, estimated_tokens }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are a cost analysis expert. Compare LLM costs for this task.\n\nTask: ${task}\nEstimated tokens: ${estimated_tokens} (prompt + completion)\n\nCalculate costs for:\n1. **DeepSeek Chat**: $0.14/1M prompt + $0.28/1M completion\n2. **DeepSeek Reasoner**: $0.42/1M prompt + $0.42/1M completion\n3. **Claude Sonnet**: $3/1M prompt + $15/1M completion\n4. **GPT-4**: $2.50/1M prompt + $10/1M completion\n\nShow:\n- Cost breakdown per model\n- Savings percentage with DeepSeek\n- When to use which model (cost vs quality)\n\nUse the deepseek_chat tool with model: \"deepseek-chat\" for this analysis.`,\n },\n },\n ],\n })\n);\n\nserver.registerPrompt(\n 'pair_programming',\n {\n title: 'Pair Programming',\n description:\n 'Interactive coding assistant that explains reasoning for code decisions',\n argsSchema: {\n task: z.string().describe('Coding task'),\n language: z.string().describe('Programming language'),\n style: z\n .enum(['beginner', 'intermediate', 'expert'])\n .default('intermediate')\n .describe('Code complexity level'),\n },\n },\n ({ task, language, style }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are a pair programming partner. Help me write code with clear reasoning.\n\nTask: ${task}\nLanguage: ${language}\nLevel: ${style}\n\nPlease:\n1. **Plan**: Break down the task with reasoning\n2. **Code**: Write clean, commented code\n3. **Explain**: Explain each major decision\n4. **Test**: Suggest test cases with reasoning\n5. **Optimize**: Mention potential improvements\n\nUse the deepseek_chat tool with model: \"deepseek-reasoner\" for thoughtful code generation.`,\n },\n },\n ],\n })\n);\n\n// Function Calling Prompts\nserver.registerPrompt(\n 'function_call_debug',\n {\n title: 'Function Calling Debug',\n description:\n 'Debug function calling issues with DeepSeek models',\n argsSchema: {\n tools_json: z.string().describe('JSON string of tool definitions'),\n messages_json: z\n .string()\n .describe('JSON string of conversation messages'),\n error: z\n .string()\n .optional()\n .describe('Error message or unexpected behavior'),\n },\n },\n ({ tools_json, messages_json, error }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are a function calling expert. Debug this function calling setup.\n\nTool Definitions:\n\\`\\`\\`json\n${tools_json}\n\\`\\`\\`\n\nMessages:\n\\`\\`\\`json\n${messages_json}\n\\`\\`\\`\n\n${error ? `Error/Issue: ${error}` : 'The model is not calling the expected function.'}\n\nPlease analyze:\n1. **Tool Schema**: Are the tool definitions valid and well-structured?\n2. **Messages**: Are messages properly formatted for function calling?\n3. **Issue**: What might be causing the problem?\n4. **Fix**: Suggest corrected tool definitions and/or messages\n5. **Best Practices**: Tips for reliable function calling\n\nUse the deepseek_chat tool with model: \"deepseek-reasoner\" for thorough analysis.`,\n },\n },\n ],\n })\n);\n\nserver.registerPrompt(\n 'create_function_schema',\n {\n title: 'Create Function Schema',\n description:\n 'Generate JSON Schema for function calling from natural language description',\n argsSchema: {\n description: z\n .string()\n .describe('Natural language description of the function'),\n examples: z.string().optional().describe('Example inputs/outputs'),\n },\n },\n ({ description, examples }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are an expert at creating JSON Schemas for function calling. Create a tool definition from this description.\n\nFunction Description: ${description}\n${examples ? `\\nExamples:\\n${examples}` : ''}\n\nGenerate:\n1. **Tool Definition**: Complete JSON tool definition with type, function name, description, and parameters schema\n2. **Parameters**: Well-typed JSON Schema with descriptions for each parameter\n3. **Required Fields**: Which parameters are required\n4. **Example Call**: Show an example of how the model would call this function\n5. **Usage**: How to use this with the deepseek_chat tool's \\`tools\\` parameter\n\nOutput the tool definition as a JSON code block ready to use.\n\nUse the deepseek_chat tool with model: \"deepseek-reasoner\" for precise schema generation.`,\n },\n },\n ],\n })\n);\n\n// Start server with stdio transport\nasync function main() {\n console.error('[DeepSeek MCP] Starting server v1.1.0...');\n\n // Test connection\n console.error('[DeepSeek MCP] Testing API connection...');\n const isConnected = await deepseek.testConnection();\n\n if (!isConnected) {\n console.error('[DeepSeek MCP] Warning: Failed to connect to DeepSeek API');\n console.error(\n '[DeepSeek MCP] Please check your API key and internet connection'\n );\n } else {\n console.error('[DeepSeek MCP] API connection successful');\n }\n\n // Connect to stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error('[DeepSeek MCP] Server running on stdio');\n console.error(\n '[DeepSeek MCP] Available tools: deepseek_chat (with function calling support)'\n );\n console.error('[DeepSeek MCP] Available prompts: 12 reasoning templates');\n}\n\n// Error handling\nprocess.on('uncaughtException', (error) => {\n console.error('[DeepSeek MCP] Uncaught exception:', error);\n process.exit(1);\n});\n\nprocess.on('unhandledRejection', (reason, promise) => {\n console.error(\n '[DeepSeek MCP] Unhandled rejection at:',\n promise,\n 'reason:',\n reason\n );\n process.exit(1);\n});\n\n// Start the server\nmain().catch((error) => {\n console.error('[DeepSeek MCP] Fatal error:', error);\n process.exit(1);\n});\n"}, "10edfa0c29397eec520e6c282641d062efc75bb4": {"t": 1785749514.0710382, "n": 19079, "h": "c4bacee8bf8735ab", "c": "#!/usr/bin/env node\n\n/**\n * DeepSeek MCP Server\n * Model Context Protocol server for DeepSeek API integration\n *\n * This server exposes DeepSeek's chat and reasoning models as MCP tools\n * that can be used by Claude Code and other MCP clients.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod';\nimport { DeepSeekClient } from './deepseek-client.js';\nimport type { DeepSeekChatInput } from './types.js';\n\n// Get API key from environment variable\nconst DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY;\n\nif (!DEEPSEEK_API_KEY) {\n console.error('Error: DEEPSEEK_API_KEY environment variable is not set');\n console.error('Please set your DeepSeek API key:');\n console.error(' export DEEPSEEK_API_KEY=\"your-api-key-here\"');\n process.exit(1);\n}\n\n// Initialize DeepSeek client\nconst deepseek = new DeepSeekClient(DEEPSEEK_API_KEY);\n\n// Create MCP server\nconst server = new McpServer({\n name: 'deepseek-mcp-server',\n version: '1.0.0',\n});\n\n/**\n * Calculate cost for a request based on token usage\n */\nfunction calculateCost(\n promptTokens: number,\n completionTokens: number,\n model: string\n): number {\n // DeepSeek pricing (per 1M tokens)\n const pricing = {\n 'deepseek-chat': {\n prompt: 0.14, // $0.14 per 1M tokens\n completion: 0.28, // $0.28 per 1M tokens\n },\n 'deepseek-reasoner': {\n prompt: 0.55, // $0.55 per 1M tokens (updated pricing)\n completion: 2.19, // $2.19 per 1M tokens (updated pricing)\n },\n };\n\n const modelPricing = pricing[model as keyof typeof pricing] || pricing['deepseek-chat'];\n\n const promptCost = (promptTokens / 1_000_000) * modelPricing.prompt;\n const completionCost = (completionTokens / 1_000_000) * modelPricing.completion;\n\n return promptCost + completionCost;\n}\n\n/**\n * Format cost as readable string\n */\nfunction formatCost(cost: number): string {\n if (cost < 0.01) {\n return `$${cost.toFixed(4)}`;\n }\n return `$${cost.toFixed(2)}`;\n}\n\n// Define Zod schemas for input validation\nconst MessageSchema = z.object({\n role: z.enum(['system', 'user', 'assistant']),\n content: z.string(),\n});\n\nconst ChatInputSchema = z.object({\n messages: z.array(MessageSchema).min(1),\n model: z.enum(['deepseek-chat', 'deepseek-reasoner']).default('deepseek-chat'),\n temperature: z.number().min(0).max(2).optional(),\n max_tokens: z.number().min(1).max(32768).optional(),\n stream: z.boolean().optional().default(false),\n});\n\n/**\n * Tool: deepseek_chat\n *\n * Chat completion with DeepSeek models.\n * Supports both deepseek-chat and deepseek-reasoner (R1) models.\n */\nserver.registerTool(\n 'deepseek_chat',\n {\n title: 'DeepSeek Chat Completion',\n description:\n 'Chat with DeepSeek AI models. Supports deepseek-chat for general conversations and ' +\n 'deepseek-reasoner (R1) for complex reasoning tasks with chain-of-thought explanations. ' +\n 'The reasoner model provides both reasoning_content (thinking process) and content (final answer).',\n inputSchema: {\n messages: z.array(MessageSchema).min(1).describe('Array of conversation messages'),\n model: z.enum(['deepseek-chat', 'deepseek-reasoner'])\n .default('deepseek-chat')\n .describe('Model to use. deepseek-chat for general tasks, deepseek-reasoner for complex reasoning'),\n temperature: z.number()\n .min(0)\n .max(2)\n .optional()\n .describe('Sampling temperature (0-2). Higher = more random. Default: 1.0'),\n max_tokens: z.number()\n .min(1)\n .max(32768)\n .optional()\n .describe('Maximum tokens to generate. Default: model maximum'),\n stream: z.boolean()\n .optional()\n .default(false)\n .describe('Enable streaming mode. Returns full response after streaming completes.'),\n },\n outputSchema: {\n content: z.string(),\n reasoning_content: z.string().optional(),\n model: z.string(),\n usage: z.object({\n prompt_tokens: z.number(),\n completion_tokens: z.number(),\n total_tokens: z.number(),\n }),\n finish_reason: z.string(),\n },\n },\n async (input: DeepSeekChatInput) => {\n try {\n // Validate input\n const validated = ChatInputSchema.parse(input);\n\n console.error(\n `[DeepSeek MCP] Request: model=${validated.model}, messages=${validated.messages.length}, stream=${validated.stream}`\n );\n\n // Call appropriate method based on stream parameter\n const response = validated.stream\n ? await deepseek.createStreamingChatCompletion({\n model: validated.model,\n messages: validated.messages,\n temperature: validated.temperature,\n max_tokens: validated.max_tokens,\n })\n : await deepseek.createChatCompletion({\n model: validated.model,\n messages: validated.messages,\n temperature: validated.temperature,\n max_tokens: validated.max_tokens,\n });\n\n console.error(\n `[DeepSeek MCP] Response: tokens=${response.usage.total_tokens}, finish_reason=${response.finish_reason}`\n );\n\n // Format response\n let responseText = '';\n\n // Add reasoning content if available (for deepseek-reasoner)\n if (response.reasoning_content) {\n responseText += `\\n${response.reasoning_content}\\n\\n\\n`;\n }\n\n responseText += response.content;\n\n // Calculate cost\n const cost = calculateCost(\n response.usage.prompt_tokens,\n response.usage.completion_tokens,\n response.model\n );\n\n // Add usage stats with cost information\n responseText += `\\n\\n---\\n📊 **Request Information:**\\n`;\n responseText += `- **Tokens:** ${response.usage.total_tokens} (${response.usage.prompt_tokens} prompt + ${response.usage.completion_tokens} completion)\\n`;\n responseText += `- **Model:** ${response.model}\\n`;\n responseText += `- **Cost:** ${formatCost(cost)}`;\n\n return {\n content: [\n {\n type: 'text' as const,\n text: responseText,\n },\n ],\n structuredContent: {\n ...response,\n cost_usd: parseFloat(cost.toFixed(6)),\n } as unknown as Record,\n };\n } catch (error: any) {\n console.error('[DeepSeek MCP] Error:', error);\n const errorMessage = error?.message || 'Unknown error occurred';\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n }\n);\n\n/**\n * Register MCP Prompts\n * Pre-built prompt templates for common reasoning tasks\n */\n\n// Core Reasoning Prompts\nserver.registerPrompt(\n 'debug_with_reasoning',\n {\n title: 'Debug Code with Reasoning',\n description: 'Debug code issues using DeepSeek R1 reasoning model with step-by-step analysis',\n argsSchema: {\n code: z.string().describe('Code to debug'),\n error: z.string().optional().describe('Error message or description of the issue'),\n language: z.string().optional().describe('Programming language'),\n },\n },\n ({ code, error, language }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are an expert debugging assistant. Analyze this code using deep reasoning.\n\n${language ? `Language: ${language}` : ''}\n${error ? `Error/Issue: ${error}` : ''}\n\nCode:\n\\`\\`\\`\n${code}\n\\`\\`\\`\n\nPlease:\n1. Identify the bug or issue\n2. Explain your reasoning process step-by-step\n3. Suggest a fix with explanation\n4. Provide the corrected code\n\nUse the deepseek_chat tool with model: \"deepseek-reasoner\" for detailed reasoning.`,\n },\n },\n ],\n })\n);\n\nserver.registerPrompt(\n 'code_review_deep',\n {\n title: 'Deep Code Review',\n description: 'Comprehensive code review analyzing quality, security, performance, and best practices',\n argsSchema: {\n code: z.string().describe('Code to review'),\n language: z.string().optional().describe('Programming language'),\n focus: z.enum(['security', 'performance', 'quality', 'all']).default('all').describe('Review focus area'),\n },\n },\n ({ code, language, focus }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are an expert code reviewer. Perform a comprehensive code review.\n\n${language ? `Language: ${language}` : ''}\nFocus: ${focus === 'all' ? 'Security, Performance, Code Quality, Best Practices' : focus}\n\nCode:\n\\`\\`\\`\n${code}\n\\`\\`\\`\n\nFor each issue found, provide:\n1. **Issue**: What's wrong\n2. **Reasoning**: Why it's a problem\n3. **Severity**: Critical/High/Medium/Low\n4. **Fix**: How to resolve it\n\nUse the deepseek_chat tool with model: \"deepseek-reasoner\" for thorough analysis.`,\n },\n },\n ],\n })\n);\n\nserver.registerPrompt(\n 'research_synthesis',\n {\n title: 'Research & Synthesis',\n description: 'Research a topic and synthesize information into a structured report',\n argsSchema: {\n topic: z.string().describe('Topic to research'),\n context: z.string().optional().describe('Additional context or specific questions'),\n depth: z.enum(['brief', 'moderate', 'comprehensive']).default('moderate').describe('Research depth'),\n },\n },\n ({ topic, context, depth }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are a research assistant. Research and synthesize information about this topic.\n\nTopic: ${topic}\n${context ? `Context: ${context}` : ''}\nDepth: ${depth}\n\nPlease provide:\n1. **Overview**: Brief summary\n2. **Key Findings**: Main points with reasoning\n3. **Analysis**: Deep dive with reasoning process\n4. **Conclusion**: Synthesis and implications\n5. **Sources**: Cite reasoning steps\n\nUse the deepseek_chat tool with model: \"deepseek-reasoner\" for comprehensive analysis.`,\n },\n },\n ],\n })\n);\n\nserver.registerPrompt(\n 'strategic_planning',\n {\n title: 'Strategic Planning',\n description: 'Analyze options and create strategic plans with reasoning for each decision',\n argsSchema: {\n goal: z.string().describe('Goal or objective'),\n context: z.string().optional().describe('Situational context'),\n constraints: z.string().optional().describe('Constraints or limitations'),\n },\n },\n ({ goal, context, constraints }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are a strategic planning expert. Create a detailed plan with reasoning.\n\nGoal: ${goal}\n${context ? `Context: ${context}` : ''}\n${constraints ? `Constraints: ${constraints}` : ''}\n\nPlease provide:\n1. **Situation Analysis**: Current state with reasoning\n2. **Options**: List possible approaches\n3. **Evaluation**: Pros/cons of each option with reasoning\n4. **Recommendation**: Best approach with detailed reasoning\n5. **Action Plan**: Step-by-step plan with rationale\n\nUse the deepseek_chat tool with model: \"deepseek-reasoner\" for thorough strategic thinking.`,\n },\n },\n ],\n })\n);\n\nserver.registerPrompt(\n 'explain_like_im_five',\n {\n title: 'Explain Like I\\'m Five',\n description: 'Explain complex topics in simple terms using analogies and reasoning',\n argsSchema: {\n topic: z.string().describe('Complex topic to explain'),\n audience: z.enum(['child', 'beginner', 'intermediate']).default('beginner').describe('Target audience level'),\n },\n },\n ({ topic, audience }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are an expert explainer. Make complex topics simple and understandable.\n\nTopic: ${topic}\nAudience: ${audience}\n\nPlease:\n1. Start with a simple analogy or metaphor\n2. Break down the concept step-by-step\n3. Use everyday examples\n4. Show your reasoning for why this explanation works\n5. Build up complexity gradually if needed\n\nUse the deepseek_chat tool with model: \"deepseek-reasoner\" to ensure logical explanations.`,\n },\n },\n ],\n })\n);\n\n// Advanced Prompts\nserver.registerPrompt(\n 'mathematical_proof',\n {\n title: 'Mathematical Proof',\n description: 'Prove mathematical statements with rigorous step-by-step reasoning',\n argsSchema: {\n statement: z.string().describe('Mathematical statement to prove'),\n context: z.string().optional().describe('Mathematical context or axioms'),\n },\n },\n ({ statement, context }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are a mathematician. Provide a rigorous proof.\n\nStatement to prove: ${statement}\n${context ? `Context/Axioms: ${context}` : ''}\n\nProvide:\n1. **Given**: What we know\n2. **To Prove**: What we're proving\n3. **Proof**: Step-by-step logical reasoning\n4. **Conclusion**: QED statement\n\nUse the deepseek_chat tool with model: \"deepseek-reasoner\" for strict logical reasoning.`,\n },\n },\n ],\n })\n);\n\nserver.registerPrompt(\n 'argument_validation',\n {\n title: 'Argument Validation',\n description: 'Analyze arguments for logical fallacies and reasoning errors',\n argsSchema: {\n argument: z.string().describe('Argument to validate'),\n type: z.enum(['informal', 'formal', 'both']).default('informal').describe('Analysis type'),\n },\n },\n ({ argument, type }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are a logic expert. Analyze this argument for validity.\n\nArgument:\n${argument}\n\nAnalysis type: ${type}\n\nPlease identify:\n1. **Structure**: Break down the argument's structure\n2. **Premises**: List all premises and assumptions\n3. **Conclusion**: What's being claimed\n4. **Reasoning**: Analyze the logical flow\n5. **Fallacies**: Any logical fallacies or errors\n6. **Validity**: Is the reasoning sound?\n7. **Improvements**: How to strengthen the argument\n\nUse the deepseek_chat tool with model: \"deepseek-reasoner\" for thorough logical analysis.`,\n },\n },\n ],\n })\n);\n\nserver.registerPrompt(\n 'creative_ideation',\n {\n title: 'Creative Ideation',\n description: 'Generate creative ideas with reasoning for feasibility and value',\n argsSchema: {\n challenge: z.string().describe('Problem or challenge to solve'),\n constraints: z.string().optional().describe('Constraints or requirements'),\n quantity: z.number().min(1).max(20).default(5).describe('Number of ideas to generate'),\n },\n },\n ({ challenge, constraints, quantity }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are a creative problem solver. Generate innovative ideas with reasoning.\n\nChallenge: ${challenge}\n${constraints ? `Constraints: ${constraints}` : ''}\nIdeas needed: ${quantity}\n\nFor each idea, provide:\n1. **Idea**: The concept\n2. **Reasoning**: Why this could work\n3. **Feasibility**: How realistic it is (High/Medium/Low)\n4. **Value**: Potential impact\n5. **Next Steps**: How to validate/implement\n\nUse the deepseek_chat tool with model: \"deepseek-reasoner\" for reasoned creativity.`,\n },\n },\n ],\n })\n);\n\nserver.registerPrompt(\n 'cost_comparison',\n {\n title: 'LLM Cost Comparison',\n description: 'Compare costs of different LLMs for a task and show savings with DeepSeek',\n argsSchema: {\n task: z.string().describe('Task description'),\n estimated_tokens: z.number().min(100).describe('Estimated token count (prompt + completion)'),\n },\n },\n ({ task, estimated_tokens }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are a cost analysis expert. Compare LLM costs for this task.\n\nTask: ${task}\nEstimated tokens: ${estimated_tokens} (prompt + completion)\n\nCalculate costs for:\n1. **DeepSeek Chat**: $0.14/1M prompt + $0.28/1M completion\n2. **DeepSeek Reasoner**: $0.42/1M prompt + $0.42/1M completion\n3. **Claude Sonnet**: $3/1M prompt + $15/1M completion\n4. **GPT-4**: $2.50/1M prompt + $10/1M completion\n\nShow:\n- Cost breakdown per model\n- Savings percentage with DeepSeek\n- When to use which model (cost vs quality)\n\nUse the deepseek_chat tool with model: \"deepseek-chat\" for this analysis.`,\n },\n },\n ],\n })\n);\n\nserver.registerPrompt(\n 'pair_programming',\n {\n title: 'Pair Programming',\n description: 'Interactive coding assistant that explains reasoning for code decisions',\n argsSchema: {\n task: z.string().describe('Coding task'),\n language: z.string().describe('Programming language'),\n style: z.enum(['beginner', 'intermediate', 'expert']).default('intermediate').describe('Code complexity level'),\n },\n },\n ({ task, language, style }, _extra) => ({\n messages: [\n {\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `You are a pair programming partner. Help me write code with clear reasoning.\n\nTask: ${task}\nLanguage: ${language}\nLevel: ${style}\n\nPlease:\n1. **Plan**: Break down the task with reasoning\n2. **Code**: Write clean, commented code\n3. **Explain**: Explain each major decision\n4. **Test**: Suggest test cases with reasoning\n5. **Optimize**: Mention potential improvements\n\nUse the deepseek_chat tool with model: \"deepseek-reasoner\" for thoughtful code generation.`,\n },\n },\n ],\n })\n);\n\n// Start server with stdio transport\nasync function main() {\n console.error('[DeepSeek MCP] Starting server...');\n\n // Test connection\n console.error('[DeepSeek MCP] Testing API connection...');\n const isConnected = await deepseek.testConnection();\n\n if (!isConnected) {\n console.error('[DeepSeek MCP] Warning: Failed to connect to DeepSeek API');\n console.error('[DeepSeek MCP] Please check your API key and internet connection');\n } else {\n console.error('[DeepSeek MCP] API connection successful');\n }\n\n // Connect to stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error('[DeepSeek MCP] Server running on stdio');\n console.error('[DeepSeek MCP] Available tools: deepseek_chat');\n console.error('[DeepSeek MCP] Available prompts: 10 reasoning templates');\n}\n\n// Error handling\nprocess.on('uncaughtException', (error) => {\n console.error('[DeepSeek MCP] Uncaught exception:', error);\n process.exit(1);\n});\n\nprocess.on('unhandledRejection', (reason, promise) => {\n console.error('[DeepSeek MCP] Unhandled rejection at:', promise, 'reason:', reason);\n process.exit(1);\n});\n\n// Start the server\nmain().catch((error) => {\n console.error('[DeepSeek MCP] Fatal error:', error);\n process.exit(1);\n});\n"}, "842e380df8bd77c4330c5fc26a9130dc28e1bf66": {"t": 1785749515.1569595, "n": 6514, "h": "b611ddbbc54753da", "c": "#!/usr/bin/env node\n\n/**\n * DeepSeek MCP Server\n * Model Context Protocol server for DeepSeek API integration\n *\n * This server exposes DeepSeek's chat and reasoning models as MCP tools\n * that can be used by Claude Code and other MCP clients.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod';\nimport { DeepSeekClient } from './deepseek-client.js';\nimport type { DeepSeekChatInput } from './types.js';\n\n// Get API key from environment variable\nconst DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY;\n\nif (!DEEPSEEK_API_KEY) {\n console.error('Error: DEEPSEEK_API_KEY environment variable is not set');\n console.error('Please set your DeepSeek API key:');\n console.error(' export DEEPSEEK_API_KEY=\"your-api-key-here\"');\n process.exit(1);\n}\n\n// Initialize DeepSeek client\nconst deepseek = new DeepSeekClient(DEEPSEEK_API_KEY);\n\n// Create MCP server\nconst server = new McpServer({\n name: 'deepseek-mcp-server',\n version: '1.0.0',\n});\n\n// Define Zod schemas for input validation\nconst MessageSchema = z.object({\n role: z.enum(['system', 'user', 'assistant']),\n content: z.string(),\n});\n\nconst ChatInputSchema = z.object({\n messages: z.array(MessageSchema).min(1),\n model: z.enum(['deepseek-chat', 'deepseek-reasoner']).default('deepseek-chat'),\n temperature: z.number().min(0).max(2).optional(),\n max_tokens: z.number().min(1).max(32768).optional(),\n stream: z.boolean().optional().default(false),\n});\n\n/**\n * Tool: deepseek_chat\n *\n * Chat completion with DeepSeek models.\n * Supports both deepseek-chat and deepseek-reasoner (R1) models.\n */\nserver.registerTool(\n 'deepseek_chat',\n {\n title: 'DeepSeek Chat Completion',\n description:\n 'Chat with DeepSeek AI models. Supports deepseek-chat for general conversations and ' +\n 'deepseek-reasoner (R1) for complex reasoning tasks with chain-of-thought explanations. ' +\n 'The reasoner model provides both reasoning_content (thinking process) and content (final answer).',\n inputSchema: {\n messages: z.array(MessageSchema).min(1).describe('Array of conversation messages'),\n model: z.enum(['deepseek-chat', 'deepseek-reasoner'])\n .default('deepseek-chat')\n .describe('Model to use. deepseek-chat for general tasks, deepseek-reasoner for complex reasoning'),\n temperature: z.number()\n .min(0)\n .max(2)\n .optional()\n .describe('Sampling temperature (0-2). Higher = more random. Default: 1.0'),\n max_tokens: z.number()\n .min(1)\n .max(32768)\n .optional()\n .describe('Maximum tokens to generate. Default: model maximum'),\n stream: z.boolean()\n .optional()\n .default(false)\n .describe('Enable streaming mode. Returns full response after streaming completes.'),\n },\n outputSchema: {\n content: z.string(),\n reasoning_content: z.string().optional(),\n model: z.string(),\n usage: z.object({\n prompt_tokens: z.number(),\n completion_tokens: z.number(),\n total_tokens: z.number(),\n }),\n finish_reason: z.string(),\n },\n },\n async (input: DeepSeekChatInput) => {\n try {\n // Validate input\n const validated = ChatInputSchema.parse(input);\n\n console.error(\n `[DeepSeek MCP] Request: model=${validated.model}, messages=${validated.messages.length}, stream=${validated.stream}`\n );\n\n // Call appropriate method based on stream parameter\n const response = validated.stream\n ? await deepseek.createStreamingChatCompletion({\n model: validated.model,\n messages: validated.messages,\n temperature: validated.temperature,\n max_tokens: validated.max_tokens,\n })\n : await deepseek.createChatCompletion({\n model: validated.model,\n messages: validated.messages,\n temperature: validated.temperature,\n max_tokens: validated.max_tokens,\n });\n\n console.error(\n `[DeepSeek MCP] Response: tokens=${response.usage.total_tokens}, finish_reason=${response.finish_reason}`\n );\n\n // Format response\n let responseText = '';\n\n // Add reasoning content if available (for deepseek-reasoner)\n if (response.reasoning_content) {\n responseText += `\\n${response.reasoning_content}\\n\\n\\n`;\n }\n\n responseText += response.content;\n\n // Add usage stats\n responseText += `\\n\\n---\\n**Model:** ${response.model}\\n`;\n responseText += `**Tokens:** ${response.usage.prompt_tokens} prompt + ${response.usage.completion_tokens} completion = ${response.usage.total_tokens} total`;\n\n return {\n content: [\n {\n type: 'text',\n text: responseText,\n },\n ],\n structuredContent: response as unknown as Record,\n };\n } catch (error: any) {\n console.error('[DeepSeek MCP] Error:', error);\n const errorMessage = error?.message || 'Unknown error occurred';\n\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n }\n);\n\n// Start server with stdio transport\nasync function main() {\n console.error('[DeepSeek MCP] Starting server...');\n\n // Test connection\n console.error('[DeepSeek MCP] Testing API connection...');\n const isConnected = await deepseek.testConnection();\n\n if (!isConnected) {\n console.error('[DeepSeek MCP] Warning: Failed to connect to DeepSeek API');\n console.error('[DeepSeek MCP] Please check your API key and internet connection');\n } else {\n console.error('[DeepSeek MCP] API connection successful');\n }\n\n // Connect to stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error('[DeepSeek MCP] Server running on stdio');\n console.error('[DeepSeek MCP] Available tools: deepseek_chat');\n}\n\n// Error handling\nprocess.on('uncaughtException', (error) => {\n console.error('[DeepSeek MCP] Uncaught exception:', error);\n process.exit(1);\n});\n\nprocess.on('unhandledRejection', (reason, promise) => {\n console.error('[DeepSeek MCP] Unhandled rejection at:', promise, 'reason:', reason);\n process.exit(1);\n});\n\n// Start the server\nmain().catch((error) => {\n console.error('[DeepSeek MCP] Fatal error:', error);\n process.exit(1);\n});\n"}}, "worker/src/index.ts": {"5eb18b60abee16172cb9d04e70b4c46fd470783a": {"t": 1785746050.4687738, "n": 22675, "h": "850321e58192039d", "c": "/**\n * DeepSeek MCP Server — Cloudflare Worker\n * Stateless Streamable HTTP transport with BYOK (Bring Your Own Key)\n *\n * Users send their own DeepSeek API key as Bearer token.\n * The worker proxies MCP requests to DeepSeek API.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';\nimport { z } from 'zod';\n\nconst VERSION = '2.1.0';\nconst DEEPSEEK_API_URL = 'https://api.deepseek.com/chat/completions';\nconst DEEPSEEK_FIM_URL = 'https://api.deepseek.com/beta/completions';\nconst FIM_MAX_TOKENS = 4096;\n\n// Pricing per 1M tokens (DeepSeek V4). chat/reasoner aliases resolve to v4-flash.\ninterface ModelPricing {\n cacheHit: number;\n cacheMiss: number;\n output: number;\n}\nconst MODEL_PRICING: Record = {\n 'deepseek-v4-flash': { cacheHit: 0.0028, cacheMiss: 0.14, output: 0.28 },\n 'deepseek-v4-pro': { cacheHit: 0.003625, cacheMiss: 0.435, output: 0.87 },\n};\nconst DEFAULT_PRICING: ModelPricing = MODEL_PRICING['deepseek-v4-flash'];\nfunction getPricing(model: string): ModelPricing {\n return MODEL_PRICING[model] || DEFAULT_PRICING;\n}\n\nfunction extractApiKey(request: Request): string | null {\n const auth = request.headers.get('authorization');\n if (!auth?.startsWith('Bearer ')) return null;\n return auth.slice(7);\n}\n\nfunction calculateCost(usage: Record, model: string): { totalCost: number; formatted: string } {\n const pricing = getPricing(model);\n const cacheHit = usage.prompt_cache_hit_tokens || 0;\n const cacheMiss = usage.prompt_cache_miss_tokens || usage.prompt_tokens || 0;\n const output = usage.completion_tokens || 0;\n\n const totalCost =\n (cacheHit * pricing.cacheHit) / 1_000_000 +\n (cacheMiss * pricing.cacheMiss) / 1_000_000 +\n (output * pricing.output) / 1_000_000;\n\n let formatted = `$${totalCost.toFixed(6)}`;\n if (cacheHit > 0) {\n const ratio = ((cacheHit / (cacheHit + cacheMiss)) * 100).toFixed(0);\n formatted += ` (cache hit: ${ratio}%)`;\n }\n return { totalCost, formatted };\n}\n\nfunction createMcpServer(apiKey: string): McpServer {\n const server = new McpServer({ name: 'deepseek-mcp-server', version: VERSION });\n\n server.tool(\n 'deepseek_chat',\n 'Chat with DeepSeek V4 models (deepseek-v4-flash, deepseek-v4-pro; chat/reasoner accepted as aliases). Supports function calling, thinking mode, JSON output, cost tracking.',\n {\n messages: z\n .array(\n z.object({\n role: z.enum(['system', 'user', 'assistant', 'tool']),\n content: z.string(),\n tool_call_id: z.string().optional(),\n })\n )\n .min(1)\n .describe('Conversation messages'),\n model: z\n .enum(['deepseek-v4-flash', 'deepseek-v4-pro', 'deepseek-chat', 'deepseek-reasoner'])\n .default('deepseek-v4-flash')\n .describe('Model to use. v4-flash (default) or v4-pro, both 1M context. Non-thinking by default. Deprecated aliases (still accepted, prefer v4 names): deepseek-chat -> v4-flash non-thinking, deepseek-reasoner -> v4-flash thinking'),\n temperature: z.number().min(0).max(2).optional().describe('Sampling temperature (0-2). Ignored in thinking mode'),\n max_tokens: z.number().min(1).max(384000).optional().describe('Max tokens to generate (up to 384000)'),\n stream: z.boolean().default(false).describe('Enable streaming (accumulated response returned)'),\n tools: z\n .array(\n z.object({\n type: z.literal('function'),\n function: z.object({\n name: z.string().min(1),\n description: z.string().optional(),\n parameters: z.record(z.string(), z.unknown()).optional(),\n }),\n })\n )\n .max(128)\n .optional()\n .describe('Tool definitions for function calling'),\n tool_choice: z\n .union([\n z.enum(['auto', 'none', 'required']),\n z.object({\n type: z.literal('function'),\n function: z.object({ name: z.string().min(1) }),\n }),\n ])\n .optional()\n .describe('Tool choice control'),\n thinking: z\n .object({ type: z.enum(['enabled', 'disabled']) })\n .optional()\n .describe('Toggle thinking mode. {type:\"enabled\"} to reason, {type:\"disabled\"} for a fast answer (default)'),\n reasoning_effort: z\n .enum(['high', 'max'])\n .optional()\n .describe('Reasoning effort while thinking is active: high (default) or max'),\n json_mode: z.boolean().optional().describe('Enable JSON output mode'),\n },\n {\n title: 'DeepSeek Chat',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n async (params) => {\n try {\n // Resolve the user-facing model + thinking flag for the V4 API.\n // v4-flash/v4-pro are the live models; chat/reasoner are compatibility\n // aliases (retired by the API on 2026-07-24) that resolve to v4-flash.\n // The API defaults thinking to enabled, so we always send an explicit flag.\n let model: string;\n let thinking: { type: 'enabled' | 'disabled' };\n const isReasonerRouted = params.model === 'deepseek-reasoner';\n if (isReasonerRouted) {\n model = 'deepseek-v4-flash';\n thinking = { type: 'enabled' };\n } else if (params.model === 'deepseek-chat') {\n model = 'deepseek-v4-flash';\n thinking = params.thinking ?? { type: 'disabled' };\n } else {\n model = params.model;\n thinking = params.thinking ?? { type: 'disabled' };\n }\n\n const isThinkingMode = thinking.type === 'enabled';\n\n // Build DeepSeek API request body — always stream to avoid CF timeout\n const body: Record = {\n model,\n messages: params.messages,\n stream: true,\n thinking,\n };\n // Sampling params are ignored by thinking mode - don't send them\n if (params.temperature !== undefined && !isThinkingMode) body.temperature = params.temperature;\n if (params.max_tokens !== undefined) body.max_tokens = params.max_tokens;\n if (params.tools) body.tools = params.tools;\n if (params.tool_choice) body.tool_choice = params.tool_choice;\n if (isThinkingMode && params.reasoning_effort) body.reasoning_effort = params.reasoning_effort;\n if (params.json_mode) body.response_format = { type: 'json_object' };\n\n const apiResponse = await fetch(DEEPSEEK_API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!apiResponse.ok) {\n const errText = await apiResponse.text();\n return {\n content: [{ type: 'text' as const, text: `DeepSeek API Error (${apiResponse.status}): ${errText}` }],\n isError: true,\n };\n }\n\n // Read SSE stream and accumulate chunks\n let content = '';\n let reasoningContent = '';\n let responseModel = '';\n let finishReason = '';\n let usage: Record = {};\n const toolCalls: Record = {};\n\n const reader = apiResponse.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue;\n const data = line.slice(6).trim();\n if (data === '[DONE]') continue;\n\n try {\n const chunk = JSON.parse(data);\n if (chunk.model) responseModel = chunk.model;\n if (chunk.usage) usage = chunk.usage;\n\n const delta = chunk.choices?.[0]?.delta;\n if (!delta) {\n if (chunk.choices?.[0]?.finish_reason) finishReason = chunk.choices[0].finish_reason;\n continue;\n }\n\n if (delta.content) content += delta.content;\n if (delta.reasoning_content) reasoningContent += delta.reasoning_content;\n if (delta.finish_reason) finishReason = delta.finish_reason;\n\n // Accumulate tool calls\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n if (!toolCalls[idx]) {\n toolCalls[idx] = { id: tc.id || '', type: 'function', function: { name: '', arguments: '' } };\n }\n if (tc.id) toolCalls[idx].id = tc.id;\n if (tc.function?.name) toolCalls[idx].function.name += tc.function.name;\n if (tc.function?.arguments) toolCalls[idx].function.arguments += tc.function.arguments;\n }\n }\n } catch {\n // skip malformed chunks\n }\n }\n }\n\n const toolCallsArray = Object.values(toolCalls);\n const cost = calculateCost(usage, responseModel || model);\n\n // Format response text\n let text = '';\n if (reasoningContent) {\n text += `\\n${reasoningContent}\\n\\n\\n`;\n }\n text += content;\n\n if (toolCallsArray.length > 0) {\n text += '\\n\\n**Function Calls:**\\n';\n for (const tc of toolCallsArray) {\n text += `\\`${tc.function.name}\\`\\n`;\n text += `- Call ID: ${tc.id}\\n`;\n text += `- Arguments: ${tc.function.arguments}\\n\\n`;\n }\n }\n\n text += `\\n---\\n**Request Info:**\\n`;\n text += `- **Tokens:** ${usage.total_tokens || 0} (${usage.prompt_tokens || 0} prompt + ${usage.completion_tokens || 0} completion)\\n`;\n text += `- **Model:** ${responseModel || model}\\n`;\n text += `- **Cost:** ${cost.formatted}`;\n if (isReasonerRouted) {\n text += `\\n- **Routed:** deepseek-reasoner -> deepseek-v4-flash + thinking`;\n }\n\n return {\n content: [{ type: 'text' as const, text }],\n structuredContent: {\n content,\n reasoning_content: reasoningContent || undefined,\n model: responseModel || model,\n usage,\n finish_reason: finishReason,\n tool_calls: toolCallsArray.length > 0 ? toolCallsArray : undefined,\n ...(isReasonerRouted ? { routed_from: 'deepseek-reasoner' } : {}),\n cost_usd: parseFloat(cost.totalCost.toFixed(6)),\n } as unknown as Record,\n };\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${msg}` }],\n isError: true,\n };\n }\n }\n );\n\n server.tool(\n 'deepseek_fim',\n 'Fill-in-the-Middle (FIM) completion with DeepSeek V4. Provide a prompt (prefix) and an optional suffix; the model completes the text in between. Ideal for code completion and content infilling. Runs in non-thinking mode on the Beta endpoint; output is capped at 4096 tokens. The deprecated aliases deepseek-chat / deepseek-reasoner are still accepted and resolve to deepseek-v4-flash. Includes cost tracking.',\n {\n prompt: z\n .string()\n .min(1)\n .describe('The prefix text before the content to generate. For code completion, this is the code up to the cursor.'),\n suffix: z\n .string()\n .optional()\n .describe('Optional suffix text after the content to generate. The model fills the gap between prompt and suffix.'),\n model: z\n .enum(['deepseek-v4-flash', 'deepseek-v4-pro', 'deepseek-chat', 'deepseek-reasoner'])\n .default('deepseek-v4-flash')\n .describe('Model to use. v4-flash (default) or v4-pro. Deprecated aliases deepseek-chat / deepseek-reasoner are still accepted and resolve to v4-flash. FIM is always non-thinking.'),\n max_tokens: z\n .number()\n .min(1)\n .max(FIM_MAX_TOKENS)\n .optional()\n .describe(`Max tokens to generate. FIM is capped at ${FIM_MAX_TOKENS} tokens.`),\n temperature: z.number().min(0).max(2).optional().describe('Sampling temperature (0-2)'),\n stop: z\n .union([z.string(), z.array(z.string()).max(16)])\n .optional()\n .describe('Optional stop sequence(s): a single string or an array of up to 16 strings.'),\n },\n {\n title: 'DeepSeek FIM',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n async (params) => {\n try {\n // Aliases collapse to v4-flash; FIM has no thinking mode.\n const routedFrom =\n params.model === 'deepseek-chat' || params.model === 'deepseek-reasoner'\n ? params.model\n : undefined;\n const model = routedFrom ? 'deepseek-v4-flash' : params.model;\n\n const body: Record = {\n model,\n prompt: params.prompt,\n stream: false,\n };\n if (params.suffix !== undefined) body.suffix = params.suffix;\n if (params.max_tokens !== undefined) body.max_tokens = params.max_tokens;\n if (params.temperature !== undefined) body.temperature = params.temperature;\n if (params.stop !== undefined) body.stop = params.stop;\n\n const apiResponse = await fetch(DEEPSEEK_FIM_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!apiResponse.ok) {\n const errText = await apiResponse.text();\n return {\n content: [{ type: 'text' as const, text: `DeepSeek FIM API Error (${apiResponse.status}): ${errText}` }],\n isError: true,\n };\n }\n\n const json = (await apiResponse.json()) as {\n model?: string;\n choices?: Array<{ text?: string; finish_reason?: string }>;\n usage?: Record;\n };\n const choice = json.choices?.[0];\n const completion = choice?.text || '';\n const usage = json.usage || {};\n const responseModel = json.model || model;\n const cost = calculateCost(usage, responseModel);\n\n let text = completion;\n text += `\\n---\\n**Request Info:**\\n`;\n text += `- **Tokens:** ${usage.total_tokens || 0} (${usage.prompt_tokens || 0} prompt + ${usage.completion_tokens || 0} completion)\\n`;\n text += `- **Model:** ${responseModel}\\n`;\n text += `- **Cost:** ${cost.formatted}`;\n if (routedFrom) {\n text += `\\n- **Routed:** ${routedFrom} -> deepseek-v4-flash`;\n }\n\n return {\n content: [{ type: 'text' as const, text }],\n structuredContent: {\n text: completion,\n model: responseModel,\n usage,\n finish_reason: choice?.finish_reason || 'stop',\n cost_usd: parseFloat(cost.totalCost.toFixed(6)),\n ...(routedFrom ? { routed_from: routedFrom } : {}),\n } as unknown as Record,\n };\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${msg}` }],\n isError: true,\n };\n }\n }\n );\n\n return server;\n}\n\nexport default {\n async fetch(request: Request, _env: Env): Promise {\n const url = new URL(request.url);\n\n // CORS preflight\n if (request.method === 'OPTIONS') {\n return new Response(null, {\n headers: {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization, Mcp-Session-Id',\n },\n });\n }\n\n // MCP Server Card — allows Smithery and other registries to discover capabilities without scanning\n if (url.pathname === '/.well-known/mcp/server-card.json') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek V4 AI — chat, reasoning, function calling, JSON mode, fill-in-the-middle (FIM), cost tracking',\n transport: { type: 'streamable-http', url: 'https://deepseek-mcp.tahirl.com/mcp' },\n capabilities: { tools: true, prompts: false, resources: false },\n tools: [\n {\n name: 'deepseek_chat',\n description: 'Chat with DeepSeek V4 models (deepseek-v4-flash, deepseek-v4-pro; chat/reasoner accepted as aliases). Supports function calling, thinking mode, JSON output, cost tracking.',\n annotations: {\n title: 'DeepSeek Chat',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n inputSchema: {\n type: 'object',\n required: ['messages'],\n properties: {\n messages: { type: 'array', description: 'Conversation messages', items: { type: 'object' } },\n model: { type: 'string', enum: ['deepseek-v4-flash', 'deepseek-v4-pro', 'deepseek-chat', 'deepseek-reasoner'], default: 'deepseek-v4-flash' },\n temperature: { type: 'number', description: 'Sampling temperature (0-2)' },\n max_tokens: { type: 'number', description: 'Max tokens to generate (up to 384000)' },\n stream: { type: 'boolean', default: false },\n tools: { type: 'array', description: 'Tool definitions for function calling' },\n tool_choice: { description: 'Tool choice control' },\n thinking: { type: 'object', description: 'Toggle thinking mode' },\n reasoning_effort: { type: 'string', enum: ['high', 'max'], description: 'Reasoning effort while thinking' },\n json_mode: { type: 'boolean', description: 'Enable JSON output mode' },\n },\n },\n },\n {\n name: 'deepseek_fim',\n description: 'Fill-in-the-Middle (FIM) completion with DeepSeek V4. Provide a prompt (prefix) and an optional suffix; the model completes the text in between. Ideal for code completion and content infilling. Non-thinking, Beta endpoint, output capped at 4096 tokens.',\n annotations: {\n title: 'DeepSeek FIM',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n inputSchema: {\n type: 'object',\n required: ['prompt'],\n properties: {\n prompt: { type: 'string', description: 'Prefix text before the content to generate' },\n suffix: { type: 'string', description: 'Optional suffix text after the content to generate' },\n model: { type: 'string', enum: ['deepseek-v4-flash', 'deepseek-v4-pro', 'deepseek-chat', 'deepseek-reasoner'], default: 'deepseek-v4-flash' },\n max_tokens: { type: 'number', description: 'Max tokens to generate (up to 4096)' },\n temperature: { type: 'number', description: 'Sampling temperature (0-2)' },\n stop: { description: 'Stop sequence(s): a string or an array of up to 16 strings' },\n },\n },\n },\n ],\n auth: { type: 'bearer', description: 'DeepSeek API key as Bearer token' },\n icon: 'https://raw.githubusercontent.com/arikusi/deepseek-mcp-server/main/icon.png',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // Health check\n if (url.pathname === '/health') {\n return Response.json({\n status: 'ok',\n version: VERSION,\n transport: 'cloudflare-workers',\n timestamp: new Date().toISOString(),\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // Root — info page\n if (url.pathname === '/' && request.method === 'GET') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek AI models — BYOK (Bring Your Own Key)',\n endpoints: { mcp: '/mcp', health: '/health' },\n auth: 'Send your DeepSeek API key as Authorization: Bearer ',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // MCP endpoint\n if (url.pathname === '/mcp') {\n // Stateless mode: only POST is supported (no SSE via GET/DELETE)\n if (request.method !== 'POST') {\n return Response.json(\n { error: 'Method not allowed. Use POST for MCP requests.' },\n { status: 405, headers: { 'Access-Control-Allow-Origin': '*' } }\n );\n }\n\n // BYOK: require API key\n const apiKey = extractApiKey(request);\n if (!apiKey) {\n return Response.json(\n { error: 'Authorization required. Send your DeepSeek API key as: Authorization: Bearer ' },\n { status: 401, headers: { 'Access-Control-Allow-Origin': '*' } }\n );\n }\n\n // Stateless: create fresh server + transport per request\n const server = createMcpServer(apiKey);\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse: true,\n });\n await server.connect(transport);\n\n const response = await transport.handleRequest(request);\n\n // Add CORS headers\n const headers = new Headers(response.headers);\n headers.set('Access-Control-Allow-Origin', '*');\n headers.set('Access-Control-Expose-Headers', 'Mcp-Session-Id');\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n return Response.json({ error: 'Not Found' }, { status: 404 });\n },\n};\n"}, "a49a048da2ebf63df30077e11701b76275e42e7c": {"t": 1785749501.039174, "n": 22558, "h": "4a94fe8a758c50cd", "c": "/**\n * DeepSeek MCP Server — Cloudflare Worker\n * Stateless Streamable HTTP transport with BYOK (Bring Your Own Key)\n *\n * Users send their own DeepSeek API key as Bearer token.\n * The worker proxies MCP requests to DeepSeek API.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';\nimport { z } from 'zod';\n\nconst VERSION = '2.1.0';\nconst DEEPSEEK_API_URL = 'https://api.deepseek.com/chat/completions';\nconst DEEPSEEK_FIM_URL = 'https://api.deepseek.com/beta/completions';\nconst FIM_MAX_TOKENS = 4096;\n\n// Pricing per 1M tokens (DeepSeek V4). chat/reasoner aliases resolve to v4-flash.\ninterface ModelPricing {\n cacheHit: number;\n cacheMiss: number;\n output: number;\n}\nconst MODEL_PRICING: Record = {\n 'deepseek-v4-flash': { cacheHit: 0.0028, cacheMiss: 0.14, output: 0.28 },\n 'deepseek-v4-pro': { cacheHit: 0.003625, cacheMiss: 0.435, output: 0.87 },\n};\nconst DEFAULT_PRICING: ModelPricing = MODEL_PRICING['deepseek-v4-flash'];\nfunction getPricing(model: string): ModelPricing {\n return MODEL_PRICING[model] || DEFAULT_PRICING;\n}\n\nfunction extractApiKey(request: Request): string | null {\n const auth = request.headers.get('authorization');\n if (!auth?.startsWith('Bearer ')) return null;\n return auth.slice(7);\n}\n\nfunction calculateCost(usage: Record, model: string): { totalCost: number; formatted: string } {\n const pricing = getPricing(model);\n const cacheHit = usage.prompt_cache_hit_tokens || 0;\n const cacheMiss = usage.prompt_cache_miss_tokens || usage.prompt_tokens || 0;\n const output = usage.completion_tokens || 0;\n\n const totalCost =\n (cacheHit * pricing.cacheHit) / 1_000_000 +\n (cacheMiss * pricing.cacheMiss) / 1_000_000 +\n (output * pricing.output) / 1_000_000;\n\n let formatted = `$${totalCost.toFixed(6)}`;\n if (cacheHit > 0) {\n const ratio = ((cacheHit / (cacheHit + cacheMiss)) * 100).toFixed(0);\n formatted += ` (cache hit: ${ratio}%)`;\n }\n return { totalCost, formatted };\n}\n\nfunction createMcpServer(apiKey: string): McpServer {\n const server = new McpServer({ name: 'deepseek-mcp-server', version: VERSION });\n\n server.tool(\n 'deepseek_chat',\n 'Chat with DeepSeek V4 models (deepseek-v4-flash, deepseek-v4-pro; chat/reasoner accepted as aliases). Supports function calling, thinking mode, JSON output, cost tracking.',\n {\n messages: z\n .array(\n z.object({\n role: z.enum(['system', 'user', 'assistant', 'tool']),\n content: z.string(),\n tool_call_id: z.string().optional(),\n })\n )\n .min(1)\n .describe('Conversation messages'),\n model: z\n .enum(['deepseek-v4-flash', 'deepseek-v4-pro', 'deepseek-chat', 'deepseek-reasoner'])\n .default('deepseek-v4-flash')\n .describe('Model to use. v4-flash (default) or v4-pro, both 1M context. Non-thinking by default; aliases: deepseek-chat -> v4-flash non-thinking, deepseek-reasoner -> v4-flash thinking'),\n temperature: z.number().min(0).max(2).optional().describe('Sampling temperature (0-2). Ignored in thinking mode'),\n max_tokens: z.number().min(1).max(384000).optional().describe('Max tokens to generate (up to 384000)'),\n stream: z.boolean().default(false).describe('Enable streaming (accumulated response returned)'),\n tools: z\n .array(\n z.object({\n type: z.literal('function'),\n function: z.object({\n name: z.string().min(1),\n description: z.string().optional(),\n parameters: z.record(z.string(), z.unknown()).optional(),\n }),\n })\n )\n .max(128)\n .optional()\n .describe('Tool definitions for function calling'),\n tool_choice: z\n .union([\n z.enum(['auto', 'none', 'required']),\n z.object({\n type: z.literal('function'),\n function: z.object({ name: z.string().min(1) }),\n }),\n ])\n .optional()\n .describe('Tool choice control'),\n thinking: z\n .object({ type: z.enum(['enabled', 'disabled']) })\n .optional()\n .describe('Toggle thinking mode. {type:\"enabled\"} to reason, {type:\"disabled\"} for a fast answer (default)'),\n reasoning_effort: z\n .enum(['high', 'max'])\n .optional()\n .describe('Reasoning effort while thinking is active: high (default) or max'),\n json_mode: z.boolean().optional().describe('Enable JSON output mode'),\n },\n {\n title: 'DeepSeek Chat',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n async (params) => {\n try {\n // Resolve the user-facing model + thinking flag for the V4 API.\n // v4-flash/v4-pro are the live models; chat/reasoner are compatibility\n // aliases (retired by the API on 2026-07-24) that resolve to v4-flash.\n // The API defaults thinking to enabled, so we always send an explicit flag.\n let model: string;\n let thinking: { type: 'enabled' | 'disabled' };\n const isReasonerRouted = params.model === 'deepseek-reasoner';\n if (isReasonerRouted) {\n model = 'deepseek-v4-flash';\n thinking = { type: 'enabled' };\n } else if (params.model === 'deepseek-chat') {\n model = 'deepseek-v4-flash';\n thinking = params.thinking ?? { type: 'disabled' };\n } else {\n model = params.model;\n thinking = params.thinking ?? { type: 'disabled' };\n }\n\n const isThinkingMode = thinking.type === 'enabled';\n\n // Build DeepSeek API request body — always stream to avoid CF timeout\n const body: Record = {\n model,\n messages: params.messages,\n stream: true,\n thinking,\n };\n // Sampling params are ignored by thinking mode - don't send them\n if (params.temperature !== undefined && !isThinkingMode) body.temperature = params.temperature;\n if (params.max_tokens !== undefined) body.max_tokens = params.max_tokens;\n if (params.tools) body.tools = params.tools;\n if (params.tool_choice) body.tool_choice = params.tool_choice;\n if (isThinkingMode && params.reasoning_effort) body.reasoning_effort = params.reasoning_effort;\n if (params.json_mode) body.response_format = { type: 'json_object' };\n\n const apiResponse = await fetch(DEEPSEEK_API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!apiResponse.ok) {\n const errText = await apiResponse.text();\n return {\n content: [{ type: 'text' as const, text: `DeepSeek API Error (${apiResponse.status}): ${errText}` }],\n isError: true,\n };\n }\n\n // Read SSE stream and accumulate chunks\n let content = '';\n let reasoningContent = '';\n let responseModel = '';\n let finishReason = '';\n let usage: Record = {};\n const toolCalls: Record = {};\n\n const reader = apiResponse.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue;\n const data = line.slice(6).trim();\n if (data === '[DONE]') continue;\n\n try {\n const chunk = JSON.parse(data);\n if (chunk.model) responseModel = chunk.model;\n if (chunk.usage) usage = chunk.usage;\n\n const delta = chunk.choices?.[0]?.delta;\n if (!delta) {\n if (chunk.choices?.[0]?.finish_reason) finishReason = chunk.choices[0].finish_reason;\n continue;\n }\n\n if (delta.content) content += delta.content;\n if (delta.reasoning_content) reasoningContent += delta.reasoning_content;\n if (delta.finish_reason) finishReason = delta.finish_reason;\n\n // Accumulate tool calls\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n if (!toolCalls[idx]) {\n toolCalls[idx] = { id: tc.id || '', type: 'function', function: { name: '', arguments: '' } };\n }\n if (tc.id) toolCalls[idx].id = tc.id;\n if (tc.function?.name) toolCalls[idx].function.name += tc.function.name;\n if (tc.function?.arguments) toolCalls[idx].function.arguments += tc.function.arguments;\n }\n }\n } catch {\n // skip malformed chunks\n }\n }\n }\n\n const toolCallsArray = Object.values(toolCalls);\n const cost = calculateCost(usage, responseModel || model);\n\n // Format response text\n let text = '';\n if (reasoningContent) {\n text += `\\n${reasoningContent}\\n\\n\\n`;\n }\n text += content;\n\n if (toolCallsArray.length > 0) {\n text += '\\n\\n**Function Calls:**\\n';\n for (const tc of toolCallsArray) {\n text += `\\`${tc.function.name}\\`\\n`;\n text += `- Call ID: ${tc.id}\\n`;\n text += `- Arguments: ${tc.function.arguments}\\n\\n`;\n }\n }\n\n text += `\\n---\\n**Request Info:**\\n`;\n text += `- **Tokens:** ${usage.total_tokens || 0} (${usage.prompt_tokens || 0} prompt + ${usage.completion_tokens || 0} completion)\\n`;\n text += `- **Model:** ${responseModel || model}\\n`;\n text += `- **Cost:** ${cost.formatted}`;\n if (isReasonerRouted) {\n text += `\\n- **Routed:** deepseek-reasoner -> deepseek-v4-flash + thinking`;\n }\n\n return {\n content: [{ type: 'text' as const, text }],\n structuredContent: {\n content,\n reasoning_content: reasoningContent || undefined,\n model: responseModel || model,\n usage,\n finish_reason: finishReason,\n tool_calls: toolCallsArray.length > 0 ? toolCallsArray : undefined,\n ...(isReasonerRouted ? { routed_from: 'deepseek-reasoner' } : {}),\n cost_usd: parseFloat(cost.totalCost.toFixed(6)),\n } as unknown as Record,\n };\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${msg}` }],\n isError: true,\n };\n }\n }\n );\n\n server.tool(\n 'deepseek_fim',\n 'Fill-in-the-Middle (FIM) completion with DeepSeek V4. Provide a prompt (prefix) and an optional suffix; the model completes the text in between. Ideal for code completion and content infilling. Runs in non-thinking mode on the Beta endpoint; output is capped at 4096 tokens. Aliases deepseek-chat / deepseek-reasoner resolve to deepseek-v4-flash. Includes cost tracking.',\n {\n prompt: z\n .string()\n .min(1)\n .describe('The prefix text before the content to generate. For code completion, this is the code up to the cursor.'),\n suffix: z\n .string()\n .optional()\n .describe('Optional suffix text after the content to generate. The model fills the gap between prompt and suffix.'),\n model: z\n .enum(['deepseek-v4-flash', 'deepseek-v4-pro', 'deepseek-chat', 'deepseek-reasoner'])\n .default('deepseek-v4-flash')\n .describe('Model to use. v4-flash (default) or v4-pro. Aliases deepseek-chat / deepseek-reasoner resolve to v4-flash. FIM is always non-thinking.'),\n max_tokens: z\n .number()\n .min(1)\n .max(FIM_MAX_TOKENS)\n .optional()\n .describe(`Max tokens to generate. FIM is capped at ${FIM_MAX_TOKENS} tokens.`),\n temperature: z.number().min(0).max(2).optional().describe('Sampling temperature (0-2)'),\n stop: z\n .union([z.string(), z.array(z.string()).max(16)])\n .optional()\n .describe('Optional stop sequence(s): a single string or an array of up to 16 strings.'),\n },\n {\n title: 'DeepSeek FIM',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n async (params) => {\n try {\n // Aliases collapse to v4-flash; FIM has no thinking mode.\n const routedFrom =\n params.model === 'deepseek-chat' || params.model === 'deepseek-reasoner'\n ? params.model\n : undefined;\n const model = routedFrom ? 'deepseek-v4-flash' : params.model;\n\n const body: Record = {\n model,\n prompt: params.prompt,\n stream: false,\n };\n if (params.suffix !== undefined) body.suffix = params.suffix;\n if (params.max_tokens !== undefined) body.max_tokens = params.max_tokens;\n if (params.temperature !== undefined) body.temperature = params.temperature;\n if (params.stop !== undefined) body.stop = params.stop;\n\n const apiResponse = await fetch(DEEPSEEK_FIM_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!apiResponse.ok) {\n const errText = await apiResponse.text();\n return {\n content: [{ type: 'text' as const, text: `DeepSeek FIM API Error (${apiResponse.status}): ${errText}` }],\n isError: true,\n };\n }\n\n const json = (await apiResponse.json()) as {\n model?: string;\n choices?: Array<{ text?: string; finish_reason?: string }>;\n usage?: Record;\n };\n const choice = json.choices?.[0];\n const completion = choice?.text || '';\n const usage = json.usage || {};\n const responseModel = json.model || model;\n const cost = calculateCost(usage, responseModel);\n\n let text = completion;\n text += `\\n---\\n**Request Info:**\\n`;\n text += `- **Tokens:** ${usage.total_tokens || 0} (${usage.prompt_tokens || 0} prompt + ${usage.completion_tokens || 0} completion)\\n`;\n text += `- **Model:** ${responseModel}\\n`;\n text += `- **Cost:** ${cost.formatted}`;\n if (routedFrom) {\n text += `\\n- **Routed:** ${routedFrom} -> deepseek-v4-flash`;\n }\n\n return {\n content: [{ type: 'text' as const, text }],\n structuredContent: {\n text: completion,\n model: responseModel,\n usage,\n finish_reason: choice?.finish_reason || 'stop',\n cost_usd: parseFloat(cost.totalCost.toFixed(6)),\n ...(routedFrom ? { routed_from: routedFrom } : {}),\n } as unknown as Record,\n };\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${msg}` }],\n isError: true,\n };\n }\n }\n );\n\n return server;\n}\n\nexport default {\n async fetch(request: Request, _env: Env): Promise {\n const url = new URL(request.url);\n\n // CORS preflight\n if (request.method === 'OPTIONS') {\n return new Response(null, {\n headers: {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization, Mcp-Session-Id',\n },\n });\n }\n\n // MCP Server Card — allows Smithery and other registries to discover capabilities without scanning\n if (url.pathname === '/.well-known/mcp/server-card.json') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek V4 AI — chat, reasoning, function calling, JSON mode, fill-in-the-middle (FIM), cost tracking',\n transport: { type: 'streamable-http', url: 'https://deepseek-mcp.tahirl.com/mcp' },\n capabilities: { tools: true, prompts: false, resources: false },\n tools: [\n {\n name: 'deepseek_chat',\n description: 'Chat with DeepSeek V4 models (deepseek-v4-flash, deepseek-v4-pro; chat/reasoner accepted as aliases). Supports function calling, thinking mode, JSON output, cost tracking.',\n annotations: {\n title: 'DeepSeek Chat',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n inputSchema: {\n type: 'object',\n required: ['messages'],\n properties: {\n messages: { type: 'array', description: 'Conversation messages', items: { type: 'object' } },\n model: { type: 'string', enum: ['deepseek-v4-flash', 'deepseek-v4-pro', 'deepseek-chat', 'deepseek-reasoner'], default: 'deepseek-v4-flash' },\n temperature: { type: 'number', description: 'Sampling temperature (0-2)' },\n max_tokens: { type: 'number', description: 'Max tokens to generate (up to 384000)' },\n stream: { type: 'boolean', default: false },\n tools: { type: 'array', description: 'Tool definitions for function calling' },\n tool_choice: { description: 'Tool choice control' },\n thinking: { type: 'object', description: 'Toggle thinking mode' },\n reasoning_effort: { type: 'string', enum: ['high', 'max'], description: 'Reasoning effort while thinking' },\n json_mode: { type: 'boolean', description: 'Enable JSON output mode' },\n },\n },\n },\n {\n name: 'deepseek_fim',\n description: 'Fill-in-the-Middle (FIM) completion with DeepSeek V4. Provide a prompt (prefix) and an optional suffix; the model completes the text in between. Ideal for code completion and content infilling. Non-thinking, Beta endpoint, output capped at 4096 tokens.',\n annotations: {\n title: 'DeepSeek FIM',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n inputSchema: {\n type: 'object',\n required: ['prompt'],\n properties: {\n prompt: { type: 'string', description: 'Prefix text before the content to generate' },\n suffix: { type: 'string', description: 'Optional suffix text after the content to generate' },\n model: { type: 'string', enum: ['deepseek-v4-flash', 'deepseek-v4-pro', 'deepseek-chat', 'deepseek-reasoner'], default: 'deepseek-v4-flash' },\n max_tokens: { type: 'number', description: 'Max tokens to generate (up to 4096)' },\n temperature: { type: 'number', description: 'Sampling temperature (0-2)' },\n stop: { description: 'Stop sequence(s): a string or an array of up to 16 strings' },\n },\n },\n },\n ],\n auth: { type: 'bearer', description: 'DeepSeek API key as Bearer token' },\n icon: 'https://raw.githubusercontent.com/arikusi/deepseek-mcp-server/main/icon.png',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // Health check\n if (url.pathname === '/health') {\n return Response.json({\n status: 'ok',\n version: VERSION,\n transport: 'cloudflare-workers',\n timestamp: new Date().toISOString(),\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // Root — info page\n if (url.pathname === '/' && request.method === 'GET') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek AI models — BYOK (Bring Your Own Key)',\n endpoints: { mcp: '/mcp', health: '/health' },\n auth: 'Send your DeepSeek API key as Authorization: Bearer ',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // MCP endpoint\n if (url.pathname === '/mcp') {\n // Stateless mode: only POST is supported (no SSE via GET/DELETE)\n if (request.method !== 'POST') {\n return Response.json(\n { error: 'Method not allowed. Use POST for MCP requests.' },\n { status: 405, headers: { 'Access-Control-Allow-Origin': '*' } }\n );\n }\n\n // BYOK: require API key\n const apiKey = extractApiKey(request);\n if (!apiKey) {\n return Response.json(\n { error: 'Authorization required. Send your DeepSeek API key as: Authorization: Bearer ' },\n { status: 401, headers: { 'Access-Control-Allow-Origin': '*' } }\n );\n }\n\n // Stateless: create fresh server + transport per request\n const server = createMcpServer(apiKey);\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse: true,\n });\n await server.connect(transport);\n\n const response = await transport.handleRequest(request);\n\n // Add CORS headers\n const headers = new Headers(response.headers);\n headers.set('Access-Control-Allow-Origin', '*');\n headers.set('Access-Control-Expose-Headers', 'Mcp-Session-Id');\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n return Response.json({ error: 'Not Found' }, { status: 404 });\n },\n};\n"}, "f54b61e09e3baf2233e65c66c830ff43d4a87d46": {"t": 1785749502.427598, "n": 22576, "h": "2841913a772136e7", "c": "/**\n * DeepSeek MCP Server — Cloudflare Worker\n * Stateless Streamable HTTP transport with BYOK (Bring Your Own Key)\n *\n * Users send their own DeepSeek API key as Bearer token.\n * The worker proxies MCP requests to DeepSeek API.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';\nimport { z } from 'zod';\n\nconst VERSION = '2.1.0';\nconst DEEPSEEK_API_URL = 'https://api.deepseek.com/chat/completions';\nconst DEEPSEEK_FIM_URL = 'https://api.deepseek.com/beta/completions';\nconst FIM_MAX_TOKENS = 4096;\n\n// Pricing per 1M tokens (DeepSeek V4). chat/reasoner aliases resolve to v4-flash.\ninterface ModelPricing {\n cacheHit: number;\n cacheMiss: number;\n output: number;\n}\nconst MODEL_PRICING: Record = {\n 'deepseek-v4-flash': { cacheHit: 0.0028, cacheMiss: 0.14, output: 0.28 },\n 'deepseek-v4-pro': { cacheHit: 0.003625, cacheMiss: 0.435, output: 0.87 },\n};\nconst DEFAULT_PRICING: ModelPricing = MODEL_PRICING['deepseek-v4-flash'];\nfunction getPricing(model: string): ModelPricing {\n return MODEL_PRICING[model] || DEFAULT_PRICING;\n}\n\ninterface Env {}\n\nfunction extractApiKey(request: Request): string | null {\n const auth = request.headers.get('authorization');\n if (!auth?.startsWith('Bearer ')) return null;\n return auth.slice(7);\n}\n\nfunction calculateCost(usage: Record, model: string): { totalCost: number; formatted: string } {\n const pricing = getPricing(model);\n const cacheHit = usage.prompt_cache_hit_tokens || 0;\n const cacheMiss = usage.prompt_cache_miss_tokens || usage.prompt_tokens || 0;\n const output = usage.completion_tokens || 0;\n\n const totalCost =\n (cacheHit * pricing.cacheHit) / 1_000_000 +\n (cacheMiss * pricing.cacheMiss) / 1_000_000 +\n (output * pricing.output) / 1_000_000;\n\n let formatted = `$${totalCost.toFixed(6)}`;\n if (cacheHit > 0) {\n const ratio = ((cacheHit / (cacheHit + cacheMiss)) * 100).toFixed(0);\n formatted += ` (cache hit: ${ratio}%)`;\n }\n return { totalCost, formatted };\n}\n\nfunction createMcpServer(apiKey: string): McpServer {\n const server = new McpServer({ name: 'deepseek-mcp-server', version: VERSION });\n\n server.tool(\n 'deepseek_chat',\n 'Chat with DeepSeek V4 models (deepseek-v4-flash, deepseek-v4-pro; chat/reasoner accepted as aliases). Supports function calling, thinking mode, JSON output, cost tracking.',\n {\n messages: z\n .array(\n z.object({\n role: z.enum(['system', 'user', 'assistant', 'tool']),\n content: z.string(),\n tool_call_id: z.string().optional(),\n })\n )\n .min(1)\n .describe('Conversation messages'),\n model: z\n .enum(['deepseek-v4-flash', 'deepseek-v4-pro', 'deepseek-chat', 'deepseek-reasoner'])\n .default('deepseek-v4-flash')\n .describe('Model to use. v4-flash (default) or v4-pro, both 1M context. Non-thinking by default; aliases: deepseek-chat -> v4-flash non-thinking, deepseek-reasoner -> v4-flash thinking'),\n temperature: z.number().min(0).max(2).optional().describe('Sampling temperature (0-2). Ignored in thinking mode'),\n max_tokens: z.number().min(1).max(384000).optional().describe('Max tokens to generate (up to 384000)'),\n stream: z.boolean().default(false).describe('Enable streaming (accumulated response returned)'),\n tools: z\n .array(\n z.object({\n type: z.literal('function'),\n function: z.object({\n name: z.string().min(1),\n description: z.string().optional(),\n parameters: z.record(z.string(), z.unknown()).optional(),\n }),\n })\n )\n .max(128)\n .optional()\n .describe('Tool definitions for function calling'),\n tool_choice: z\n .union([\n z.enum(['auto', 'none', 'required']),\n z.object({\n type: z.literal('function'),\n function: z.object({ name: z.string().min(1) }),\n }),\n ])\n .optional()\n .describe('Tool choice control'),\n thinking: z\n .object({ type: z.enum(['enabled', 'disabled']) })\n .optional()\n .describe('Toggle thinking mode. {type:\"enabled\"} to reason, {type:\"disabled\"} for a fast answer (default)'),\n reasoning_effort: z\n .enum(['high', 'max'])\n .optional()\n .describe('Reasoning effort while thinking is active: high (default) or max'),\n json_mode: z.boolean().optional().describe('Enable JSON output mode'),\n },\n {\n title: 'DeepSeek Chat',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n async (params) => {\n try {\n // Resolve the user-facing model + thinking flag for the V4 API.\n // v4-flash/v4-pro are the live models; chat/reasoner are compatibility\n // aliases (retired by the API on 2026-07-24) that resolve to v4-flash.\n // The API defaults thinking to enabled, so we always send an explicit flag.\n let model: string;\n let thinking: { type: 'enabled' | 'disabled' };\n const isReasonerRouted = params.model === 'deepseek-reasoner';\n if (isReasonerRouted) {\n model = 'deepseek-v4-flash';\n thinking = { type: 'enabled' };\n } else if (params.model === 'deepseek-chat') {\n model = 'deepseek-v4-flash';\n thinking = params.thinking ?? { type: 'disabled' };\n } else {\n model = params.model;\n thinking = params.thinking ?? { type: 'disabled' };\n }\n\n const isThinkingMode = thinking.type === 'enabled';\n\n // Build DeepSeek API request body — always stream to avoid CF timeout\n const body: Record = {\n model,\n messages: params.messages,\n stream: true,\n thinking,\n };\n // Sampling params are ignored by thinking mode - don't send them\n if (params.temperature !== undefined && !isThinkingMode) body.temperature = params.temperature;\n if (params.max_tokens !== undefined) body.max_tokens = params.max_tokens;\n if (params.tools) body.tools = params.tools;\n if (params.tool_choice) body.tool_choice = params.tool_choice;\n if (isThinkingMode && params.reasoning_effort) body.reasoning_effort = params.reasoning_effort;\n if (params.json_mode) body.response_format = { type: 'json_object' };\n\n const apiResponse = await fetch(DEEPSEEK_API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!apiResponse.ok) {\n const errText = await apiResponse.text();\n return {\n content: [{ type: 'text' as const, text: `DeepSeek API Error (${apiResponse.status}): ${errText}` }],\n isError: true,\n };\n }\n\n // Read SSE stream and accumulate chunks\n let content = '';\n let reasoningContent = '';\n let responseModel = '';\n let finishReason = '';\n let usage: Record = {};\n const toolCalls: Record = {};\n\n const reader = apiResponse.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue;\n const data = line.slice(6).trim();\n if (data === '[DONE]') continue;\n\n try {\n const chunk = JSON.parse(data);\n if (chunk.model) responseModel = chunk.model;\n if (chunk.usage) usage = chunk.usage;\n\n const delta = chunk.choices?.[0]?.delta;\n if (!delta) {\n if (chunk.choices?.[0]?.finish_reason) finishReason = chunk.choices[0].finish_reason;\n continue;\n }\n\n if (delta.content) content += delta.content;\n if (delta.reasoning_content) reasoningContent += delta.reasoning_content;\n if (delta.finish_reason) finishReason = delta.finish_reason;\n\n // Accumulate tool calls\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n if (!toolCalls[idx]) {\n toolCalls[idx] = { id: tc.id || '', type: 'function', function: { name: '', arguments: '' } };\n }\n if (tc.id) toolCalls[idx].id = tc.id;\n if (tc.function?.name) toolCalls[idx].function.name += tc.function.name;\n if (tc.function?.arguments) toolCalls[idx].function.arguments += tc.function.arguments;\n }\n }\n } catch {\n // skip malformed chunks\n }\n }\n }\n\n const toolCallsArray = Object.values(toolCalls);\n const cost = calculateCost(usage, responseModel || model);\n\n // Format response text\n let text = '';\n if (reasoningContent) {\n text += `\\n${reasoningContent}\\n\\n\\n`;\n }\n text += content;\n\n if (toolCallsArray.length > 0) {\n text += '\\n\\n**Function Calls:**\\n';\n for (const tc of toolCallsArray) {\n text += `\\`${tc.function.name}\\`\\n`;\n text += `- Call ID: ${tc.id}\\n`;\n text += `- Arguments: ${tc.function.arguments}\\n\\n`;\n }\n }\n\n text += `\\n---\\n**Request Info:**\\n`;\n text += `- **Tokens:** ${usage.total_tokens || 0} (${usage.prompt_tokens || 0} prompt + ${usage.completion_tokens || 0} completion)\\n`;\n text += `- **Model:** ${responseModel || model}\\n`;\n text += `- **Cost:** ${cost.formatted}`;\n if (isReasonerRouted) {\n text += `\\n- **Routed:** deepseek-reasoner -> deepseek-v4-flash + thinking`;\n }\n\n return {\n content: [{ type: 'text' as const, text }],\n structuredContent: {\n content,\n reasoning_content: reasoningContent || undefined,\n model: responseModel || model,\n usage,\n finish_reason: finishReason,\n tool_calls: toolCallsArray.length > 0 ? toolCallsArray : undefined,\n ...(isReasonerRouted ? { routed_from: 'deepseek-reasoner' } : {}),\n cost_usd: parseFloat(cost.totalCost.toFixed(6)),\n } as unknown as Record,\n };\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${msg}` }],\n isError: true,\n };\n }\n }\n );\n\n server.tool(\n 'deepseek_fim',\n 'Fill-in-the-Middle (FIM) completion with DeepSeek V4. Provide a prompt (prefix) and an optional suffix; the model completes the text in between. Ideal for code completion and content infilling. Runs in non-thinking mode on the Beta endpoint; output is capped at 4096 tokens. Aliases deepseek-chat / deepseek-reasoner resolve to deepseek-v4-flash. Includes cost tracking.',\n {\n prompt: z\n .string()\n .min(1)\n .describe('The prefix text before the content to generate. For code completion, this is the code up to the cursor.'),\n suffix: z\n .string()\n .optional()\n .describe('Optional suffix text after the content to generate. The model fills the gap between prompt and suffix.'),\n model: z\n .enum(['deepseek-v4-flash', 'deepseek-v4-pro', 'deepseek-chat', 'deepseek-reasoner'])\n .default('deepseek-v4-flash')\n .describe('Model to use. v4-flash (default) or v4-pro. Aliases deepseek-chat / deepseek-reasoner resolve to v4-flash. FIM is always non-thinking.'),\n max_tokens: z\n .number()\n .min(1)\n .max(FIM_MAX_TOKENS)\n .optional()\n .describe(`Max tokens to generate. FIM is capped at ${FIM_MAX_TOKENS} tokens.`),\n temperature: z.number().min(0).max(2).optional().describe('Sampling temperature (0-2)'),\n stop: z\n .union([z.string(), z.array(z.string()).max(16)])\n .optional()\n .describe('Optional stop sequence(s): a single string or an array of up to 16 strings.'),\n },\n {\n title: 'DeepSeek FIM',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n async (params) => {\n try {\n // Aliases collapse to v4-flash; FIM has no thinking mode.\n const routedFrom =\n params.model === 'deepseek-chat' || params.model === 'deepseek-reasoner'\n ? params.model\n : undefined;\n const model = routedFrom ? 'deepseek-v4-flash' : params.model;\n\n const body: Record = {\n model,\n prompt: params.prompt,\n stream: false,\n };\n if (params.suffix !== undefined) body.suffix = params.suffix;\n if (params.max_tokens !== undefined) body.max_tokens = params.max_tokens;\n if (params.temperature !== undefined) body.temperature = params.temperature;\n if (params.stop !== undefined) body.stop = params.stop;\n\n const apiResponse = await fetch(DEEPSEEK_FIM_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!apiResponse.ok) {\n const errText = await apiResponse.text();\n return {\n content: [{ type: 'text' as const, text: `DeepSeek FIM API Error (${apiResponse.status}): ${errText}` }],\n isError: true,\n };\n }\n\n const json = (await apiResponse.json()) as {\n model?: string;\n choices?: Array<{ text?: string; finish_reason?: string }>;\n usage?: Record;\n };\n const choice = json.choices?.[0];\n const completion = choice?.text || '';\n const usage = json.usage || {};\n const responseModel = json.model || model;\n const cost = calculateCost(usage, responseModel);\n\n let text = completion;\n text += `\\n---\\n**Request Info:**\\n`;\n text += `- **Tokens:** ${usage.total_tokens || 0} (${usage.prompt_tokens || 0} prompt + ${usage.completion_tokens || 0} completion)\\n`;\n text += `- **Model:** ${responseModel}\\n`;\n text += `- **Cost:** ${cost.formatted}`;\n if (routedFrom) {\n text += `\\n- **Routed:** ${routedFrom} -> deepseek-v4-flash`;\n }\n\n return {\n content: [{ type: 'text' as const, text }],\n structuredContent: {\n text: completion,\n model: responseModel,\n usage,\n finish_reason: choice?.finish_reason || 'stop',\n cost_usd: parseFloat(cost.totalCost.toFixed(6)),\n ...(routedFrom ? { routed_from: routedFrom } : {}),\n } as unknown as Record,\n };\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${msg}` }],\n isError: true,\n };\n }\n }\n );\n\n return server;\n}\n\nexport default {\n async fetch(request: Request, _env: Env): Promise {\n const url = new URL(request.url);\n\n // CORS preflight\n if (request.method === 'OPTIONS') {\n return new Response(null, {\n headers: {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization, Mcp-Session-Id',\n },\n });\n }\n\n // MCP Server Card — allows Smithery and other registries to discover capabilities without scanning\n if (url.pathname === '/.well-known/mcp/server-card.json') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek V4 AI — chat, reasoning, function calling, JSON mode, fill-in-the-middle (FIM), cost tracking',\n transport: { type: 'streamable-http', url: 'https://deepseek-mcp.tahirl.com/mcp' },\n capabilities: { tools: true, prompts: false, resources: false },\n tools: [\n {\n name: 'deepseek_chat',\n description: 'Chat with DeepSeek V4 models (deepseek-v4-flash, deepseek-v4-pro; chat/reasoner accepted as aliases). Supports function calling, thinking mode, JSON output, cost tracking.',\n annotations: {\n title: 'DeepSeek Chat',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n inputSchema: {\n type: 'object',\n required: ['messages'],\n properties: {\n messages: { type: 'array', description: 'Conversation messages', items: { type: 'object' } },\n model: { type: 'string', enum: ['deepseek-v4-flash', 'deepseek-v4-pro', 'deepseek-chat', 'deepseek-reasoner'], default: 'deepseek-v4-flash' },\n temperature: { type: 'number', description: 'Sampling temperature (0-2)' },\n max_tokens: { type: 'number', description: 'Max tokens to generate (up to 384000)' },\n stream: { type: 'boolean', default: false },\n tools: { type: 'array', description: 'Tool definitions for function calling' },\n tool_choice: { description: 'Tool choice control' },\n thinking: { type: 'object', description: 'Toggle thinking mode' },\n reasoning_effort: { type: 'string', enum: ['high', 'max'], description: 'Reasoning effort while thinking' },\n json_mode: { type: 'boolean', description: 'Enable JSON output mode' },\n },\n },\n },\n {\n name: 'deepseek_fim',\n description: 'Fill-in-the-Middle (FIM) completion with DeepSeek V4. Provide a prompt (prefix) and an optional suffix; the model completes the text in between. Ideal for code completion and content infilling. Non-thinking, Beta endpoint, output capped at 4096 tokens.',\n annotations: {\n title: 'DeepSeek FIM',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n inputSchema: {\n type: 'object',\n required: ['prompt'],\n properties: {\n prompt: { type: 'string', description: 'Prefix text before the content to generate' },\n suffix: { type: 'string', description: 'Optional suffix text after the content to generate' },\n model: { type: 'string', enum: ['deepseek-v4-flash', 'deepseek-v4-pro', 'deepseek-chat', 'deepseek-reasoner'], default: 'deepseek-v4-flash' },\n max_tokens: { type: 'number', description: 'Max tokens to generate (up to 4096)' },\n temperature: { type: 'number', description: 'Sampling temperature (0-2)' },\n stop: { description: 'Stop sequence(s): a string or an array of up to 16 strings' },\n },\n },\n },\n ],\n auth: { type: 'bearer', description: 'DeepSeek API key as Bearer token' },\n icon: 'https://raw.githubusercontent.com/arikusi/deepseek-mcp-server/main/icon.png',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // Health check\n if (url.pathname === '/health') {\n return Response.json({\n status: 'ok',\n version: VERSION,\n transport: 'cloudflare-workers',\n timestamp: new Date().toISOString(),\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // Root — info page\n if (url.pathname === '/' && request.method === 'GET') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek AI models — BYOK (Bring Your Own Key)',\n endpoints: { mcp: '/mcp', health: '/health' },\n auth: 'Send your DeepSeek API key as Authorization: Bearer ',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // MCP endpoint\n if (url.pathname === '/mcp') {\n // Stateless mode: only POST is supported (no SSE via GET/DELETE)\n if (request.method !== 'POST') {\n return Response.json(\n { error: 'Method not allowed. Use POST for MCP requests.' },\n { status: 405, headers: { 'Access-Control-Allow-Origin': '*' } }\n );\n }\n\n // BYOK: require API key\n const apiKey = extractApiKey(request);\n if (!apiKey) {\n return Response.json(\n { error: 'Authorization required. Send your DeepSeek API key as: Authorization: Bearer ' },\n { status: 401, headers: { 'Access-Control-Allow-Origin': '*' } }\n );\n }\n\n // Stateless: create fresh server + transport per request\n const server = createMcpServer(apiKey);\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse: true,\n });\n await server.connect(transport);\n\n const response = await transport.handleRequest(request);\n\n // Add CORS headers\n const headers = new Headers(response.headers);\n headers.set('Access-Control-Allow-Origin', '*');\n headers.set('Access-Control-Expose-Headers', 'Mcp-Session-Id');\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n return Response.json({ error: 'Not Found' }, { status: 404 });\n },\n};\n"}, "c06a79b0b0c950af15b930e9d69b4a02262ae17b": {"t": 1785749503.7250762, "n": 16487, "h": "89f9c1c2aaed9a87", "c": "/**\n * DeepSeek MCP Server — Cloudflare Worker\n * Stateless Streamable HTTP transport with BYOK (Bring Your Own Key)\n *\n * Users send their own DeepSeek API key as Bearer token.\n * The worker proxies MCP requests to DeepSeek API.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';\nimport { z } from 'zod';\n\nconst VERSION = '2.0.0';\nconst DEEPSEEK_API_URL = 'https://api.deepseek.com/chat/completions';\n\n// Pricing per 1M tokens (DeepSeek V4). chat/reasoner aliases resolve to v4-flash.\ninterface ModelPricing {\n cacheHit: number;\n cacheMiss: number;\n output: number;\n}\nconst MODEL_PRICING: Record = {\n 'deepseek-v4-flash': { cacheHit: 0.0028, cacheMiss: 0.14, output: 0.28 },\n 'deepseek-v4-pro': { cacheHit: 0.003625, cacheMiss: 0.435, output: 0.87 },\n};\nconst DEFAULT_PRICING: ModelPricing = MODEL_PRICING['deepseek-v4-flash'];\nfunction getPricing(model: string): ModelPricing {\n return MODEL_PRICING[model] || DEFAULT_PRICING;\n}\n\ninterface Env {}\n\nfunction extractApiKey(request: Request): string | null {\n const auth = request.headers.get('authorization');\n if (!auth?.startsWith('Bearer ')) return null;\n return auth.slice(7);\n}\n\nfunction calculateCost(usage: Record, model: string): { totalCost: number; formatted: string } {\n const pricing = getPricing(model);\n const cacheHit = usage.prompt_cache_hit_tokens || 0;\n const cacheMiss = usage.prompt_cache_miss_tokens || usage.prompt_tokens || 0;\n const output = usage.completion_tokens || 0;\n\n const totalCost =\n (cacheHit * pricing.cacheHit) / 1_000_000 +\n (cacheMiss * pricing.cacheMiss) / 1_000_000 +\n (output * pricing.output) / 1_000_000;\n\n let formatted = `$${totalCost.toFixed(6)}`;\n if (cacheHit > 0) {\n const ratio = ((cacheHit / (cacheHit + cacheMiss)) * 100).toFixed(0);\n formatted += ` (cache hit: ${ratio}%)`;\n }\n return { totalCost, formatted };\n}\n\nfunction createMcpServer(apiKey: string): McpServer {\n const server = new McpServer({ name: 'deepseek-mcp-server', version: VERSION });\n\n server.tool(\n 'deepseek_chat',\n 'Chat with DeepSeek V4 models (deepseek-v4-flash, deepseek-v4-pro; chat/reasoner accepted as aliases). Supports function calling, thinking mode, JSON output, cost tracking.',\n {\n messages: z\n .array(\n z.object({\n role: z.enum(['system', 'user', 'assistant', 'tool']),\n content: z.string(),\n tool_call_id: z.string().optional(),\n })\n )\n .min(1)\n .describe('Conversation messages'),\n model: z\n .enum(['deepseek-v4-flash', 'deepseek-v4-pro', 'deepseek-chat', 'deepseek-reasoner'])\n .default('deepseek-v4-flash')\n .describe('Model to use. v4-flash (default) or v4-pro, both 1M context. Non-thinking by default; aliases: deepseek-chat -> v4-flash non-thinking, deepseek-reasoner -> v4-flash thinking'),\n temperature: z.number().min(0).max(2).optional().describe('Sampling temperature (0-2). Ignored in thinking mode'),\n max_tokens: z.number().min(1).max(384000).optional().describe('Max tokens to generate (up to 384000)'),\n stream: z.boolean().default(false).describe('Enable streaming (accumulated response returned)'),\n tools: z\n .array(\n z.object({\n type: z.literal('function'),\n function: z.object({\n name: z.string().min(1),\n description: z.string().optional(),\n parameters: z.record(z.string(), z.unknown()).optional(),\n }),\n })\n )\n .max(128)\n .optional()\n .describe('Tool definitions for function calling'),\n tool_choice: z\n .union([\n z.enum(['auto', 'none', 'required']),\n z.object({\n type: z.literal('function'),\n function: z.object({ name: z.string().min(1) }),\n }),\n ])\n .optional()\n .describe('Tool choice control'),\n thinking: z\n .object({ type: z.enum(['enabled', 'disabled']) })\n .optional()\n .describe('Toggle thinking mode. {type:\"enabled\"} to reason, {type:\"disabled\"} for a fast answer (default)'),\n reasoning_effort: z\n .enum(['high', 'max'])\n .optional()\n .describe('Reasoning effort while thinking is active: high (default) or max'),\n json_mode: z.boolean().optional().describe('Enable JSON output mode'),\n },\n {\n title: 'DeepSeek Chat',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n async (params) => {\n try {\n // Resolve the user-facing model + thinking flag for the V4 API.\n // v4-flash/v4-pro are the live models; chat/reasoner are compatibility\n // aliases (retired by the API on 2026-07-24) that resolve to v4-flash.\n // The API defaults thinking to enabled, so we always send an explicit flag.\n let model: string;\n let thinking: { type: 'enabled' | 'disabled' };\n const isReasonerRouted = params.model === 'deepseek-reasoner';\n if (isReasonerRouted) {\n model = 'deepseek-v4-flash';\n thinking = { type: 'enabled' };\n } else if (params.model === 'deepseek-chat') {\n model = 'deepseek-v4-flash';\n thinking = params.thinking ?? { type: 'disabled' };\n } else {\n model = params.model;\n thinking = params.thinking ?? { type: 'disabled' };\n }\n\n const isThinkingMode = thinking.type === 'enabled';\n\n // Build DeepSeek API request body — always stream to avoid CF timeout\n const body: Record = {\n model,\n messages: params.messages,\n stream: true,\n thinking,\n };\n // Sampling params are ignored by thinking mode - don't send them\n if (params.temperature !== undefined && !isThinkingMode) body.temperature = params.temperature;\n if (params.max_tokens !== undefined) body.max_tokens = params.max_tokens;\n if (params.tools) body.tools = params.tools;\n if (params.tool_choice) body.tool_choice = params.tool_choice;\n if (isThinkingMode && params.reasoning_effort) body.reasoning_effort = params.reasoning_effort;\n if (params.json_mode) body.response_format = { type: 'json_object' };\n\n const apiResponse = await fetch(DEEPSEEK_API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!apiResponse.ok) {\n const errText = await apiResponse.text();\n return {\n content: [{ type: 'text' as const, text: `DeepSeek API Error (${apiResponse.status}): ${errText}` }],\n isError: true,\n };\n }\n\n // Read SSE stream and accumulate chunks\n let content = '';\n let reasoningContent = '';\n let responseModel = '';\n let finishReason = '';\n let usage: Record = {};\n const toolCalls: Record = {};\n\n const reader = apiResponse.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue;\n const data = line.slice(6).trim();\n if (data === '[DONE]') continue;\n\n try {\n const chunk = JSON.parse(data);\n if (chunk.model) responseModel = chunk.model;\n if (chunk.usage) usage = chunk.usage;\n\n const delta = chunk.choices?.[0]?.delta;\n if (!delta) {\n if (chunk.choices?.[0]?.finish_reason) finishReason = chunk.choices[0].finish_reason;\n continue;\n }\n\n if (delta.content) content += delta.content;\n if (delta.reasoning_content) reasoningContent += delta.reasoning_content;\n if (delta.finish_reason) finishReason = delta.finish_reason;\n\n // Accumulate tool calls\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n if (!toolCalls[idx]) {\n toolCalls[idx] = { id: tc.id || '', type: 'function', function: { name: '', arguments: '' } };\n }\n if (tc.id) toolCalls[idx].id = tc.id;\n if (tc.function?.name) toolCalls[idx].function.name += tc.function.name;\n if (tc.function?.arguments) toolCalls[idx].function.arguments += tc.function.arguments;\n }\n }\n } catch {\n // skip malformed chunks\n }\n }\n }\n\n const toolCallsArray = Object.values(toolCalls);\n const cost = calculateCost(usage, responseModel || model);\n\n // Format response text\n let text = '';\n if (reasoningContent) {\n text += `\\n${reasoningContent}\\n\\n\\n`;\n }\n text += content;\n\n if (toolCallsArray.length > 0) {\n text += '\\n\\n**Function Calls:**\\n';\n for (const tc of toolCallsArray) {\n text += `\\`${tc.function.name}\\`\\n`;\n text += `- Call ID: ${tc.id}\\n`;\n text += `- Arguments: ${tc.function.arguments}\\n\\n`;\n }\n }\n\n text += `\\n---\\n**Request Info:**\\n`;\n text += `- **Tokens:** ${usage.total_tokens || 0} (${usage.prompt_tokens || 0} prompt + ${usage.completion_tokens || 0} completion)\\n`;\n text += `- **Model:** ${responseModel || model}\\n`;\n text += `- **Cost:** ${cost.formatted}`;\n if (isReasonerRouted) {\n text += `\\n- **Routed:** deepseek-reasoner -> deepseek-v4-flash + thinking`;\n }\n\n return {\n content: [{ type: 'text' as const, text }],\n structuredContent: {\n content,\n reasoning_content: reasoningContent || undefined,\n model: responseModel || model,\n usage,\n finish_reason: finishReason,\n tool_calls: toolCallsArray.length > 0 ? toolCallsArray : undefined,\n ...(isReasonerRouted ? { routed_from: 'deepseek-reasoner' } : {}),\n cost_usd: parseFloat(cost.totalCost.toFixed(6)),\n } as unknown as Record,\n };\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${msg}` }],\n isError: true,\n };\n }\n }\n );\n\n return server;\n}\n\nexport default {\n async fetch(request: Request, _env: Env): Promise {\n const url = new URL(request.url);\n\n // CORS preflight\n if (request.method === 'OPTIONS') {\n return new Response(null, {\n headers: {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization, Mcp-Session-Id',\n },\n });\n }\n\n // MCP Server Card — allows Smithery and other registries to discover capabilities without scanning\n if (url.pathname === '/.well-known/mcp/server-card.json') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek V4 AI — chat, reasoning, function calling, JSON mode, cost tracking',\n transport: { type: 'streamable-http', url: 'https://deepseek-mcp.tahirl.com/mcp' },\n capabilities: { tools: true, prompts: false, resources: false },\n tools: [\n {\n name: 'deepseek_chat',\n description: 'Chat with DeepSeek V4 models (deepseek-v4-flash, deepseek-v4-pro; chat/reasoner accepted as aliases). Supports function calling, thinking mode, JSON output, cost tracking.',\n annotations: {\n title: 'DeepSeek Chat',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n inputSchema: {\n type: 'object',\n required: ['messages'],\n properties: {\n messages: { type: 'array', description: 'Conversation messages', items: { type: 'object' } },\n model: { type: 'string', enum: ['deepseek-v4-flash', 'deepseek-v4-pro', 'deepseek-chat', 'deepseek-reasoner'], default: 'deepseek-v4-flash' },\n temperature: { type: 'number', description: 'Sampling temperature (0-2)' },\n max_tokens: { type: 'number', description: 'Max tokens to generate (up to 384000)' },\n stream: { type: 'boolean', default: false },\n tools: { type: 'array', description: 'Tool definitions for function calling' },\n tool_choice: { description: 'Tool choice control' },\n thinking: { type: 'object', description: 'Toggle thinking mode' },\n reasoning_effort: { type: 'string', enum: ['high', 'max'], description: 'Reasoning effort while thinking' },\n json_mode: { type: 'boolean', description: 'Enable JSON output mode' },\n },\n },\n },\n ],\n auth: { type: 'bearer', description: 'DeepSeek API key as Bearer token' },\n icon: 'https://raw.githubusercontent.com/arikusi/deepseek-mcp-server/main/icon.png',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // Health check\n if (url.pathname === '/health') {\n return Response.json({\n status: 'ok',\n version: VERSION,\n transport: 'cloudflare-workers',\n timestamp: new Date().toISOString(),\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // Root — info page\n if (url.pathname === '/' && request.method === 'GET') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek AI models — BYOK (Bring Your Own Key)',\n endpoints: { mcp: '/mcp', health: '/health' },\n auth: 'Send your DeepSeek API key as Authorization: Bearer ',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // MCP endpoint\n if (url.pathname === '/mcp') {\n // Stateless mode: only POST is supported (no SSE via GET/DELETE)\n if (request.method !== 'POST') {\n return Response.json(\n { error: 'Method not allowed. Use POST for MCP requests.' },\n { status: 405, headers: { 'Access-Control-Allow-Origin': '*' } }\n );\n }\n\n // BYOK: require API key\n const apiKey = extractApiKey(request);\n if (!apiKey) {\n return Response.json(\n { error: 'Authorization required. Send your DeepSeek API key as: Authorization: Bearer ' },\n { status: 401, headers: { 'Access-Control-Allow-Origin': '*' } }\n );\n }\n\n // Stateless: create fresh server + transport per request\n const server = createMcpServer(apiKey);\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse: true,\n });\n await server.connect(transport);\n\n const response = await transport.handleRequest(request);\n\n // Add CORS headers\n const headers = new Headers(response.headers);\n headers.set('Access-Control-Allow-Origin', '*');\n headers.set('Access-Control-Expose-Headers', 'Mcp-Session-Id');\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n return Response.json({ error: 'Not Found' }, { status: 404 });\n },\n};\n"}, "435bda78e9aae35eb57381b4ddb0e8b2657116cf": {"t": 1785749505.8471828, "n": 14731, "h": "0192ef539133a772", "c": "/**\n * DeepSeek MCP Server — Cloudflare Worker\n * Stateless Streamable HTTP transport with BYOK (Bring Your Own Key)\n *\n * Users send their own DeepSeek API key as Bearer token.\n * The worker proxies MCP requests to DeepSeek API.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';\nimport { z } from 'zod';\n\nconst VERSION = '1.6.0';\nconst DEEPSEEK_API_URL = 'https://api.deepseek.com/chat/completions';\n\n// Pricing per 1M tokens (DeepSeek V3.2 unified)\nconst PRICING = {\n cacheHit: 0.028,\n cacheMiss: 0.28,\n output: 0.42,\n};\n\ninterface Env {}\n\nfunction extractApiKey(request: Request): string | null {\n const auth = request.headers.get('authorization');\n if (!auth?.startsWith('Bearer ')) return null;\n return auth.slice(7);\n}\n\nfunction calculateCost(usage: Record): { totalCost: number; formatted: string } {\n const cacheHit = usage.prompt_cache_hit_tokens || 0;\n const cacheMiss = usage.prompt_cache_miss_tokens || usage.prompt_tokens || 0;\n const output = usage.completion_tokens || 0;\n\n const totalCost =\n (cacheHit * PRICING.cacheHit) / 1_000_000 +\n (cacheMiss * PRICING.cacheMiss) / 1_000_000 +\n (output * PRICING.output) / 1_000_000;\n\n let formatted = `$${totalCost.toFixed(6)}`;\n if (cacheHit > 0) {\n const ratio = ((cacheHit / (cacheHit + cacheMiss)) * 100).toFixed(0);\n formatted += ` (cache hit: ${ratio}%)`;\n }\n return { totalCost, formatted };\n}\n\nfunction createMcpServer(apiKey: string): McpServer {\n const server = new McpServer({ name: 'deepseek-mcp-server', version: VERSION });\n\n server.tool(\n 'deepseek_chat',\n 'Chat with DeepSeek AI models (Chat + Reasoner). Supports function calling, thinking mode, JSON output, cost tracking.',\n {\n messages: z\n .array(\n z.object({\n role: z.enum(['system', 'user', 'assistant', 'tool']),\n content: z.string(),\n tool_call_id: z.string().optional(),\n })\n )\n .min(1)\n .describe('Conversation messages'),\n model: z\n .enum(['deepseek-chat', 'deepseek-reasoner'])\n .default('deepseek-chat')\n .describe('Model to use'),\n temperature: z.number().min(0).max(2).optional().describe('Sampling temperature (0-2)'),\n max_tokens: z.number().min(1).max(65536).optional().describe('Max tokens to generate'),\n stream: z.boolean().default(false).describe('Enable streaming (accumulated response returned)'),\n tools: z\n .array(\n z.object({\n type: z.literal('function'),\n function: z.object({\n name: z.string().min(1),\n description: z.string().optional(),\n parameters: z.record(z.string(), z.unknown()).optional(),\n }),\n })\n )\n .max(128)\n .optional()\n .describe('Tool definitions for function calling'),\n tool_choice: z\n .union([\n z.enum(['auto', 'none', 'required']),\n z.object({\n type: z.literal('function'),\n function: z.object({ name: z.string().min(1) }),\n }),\n ])\n .optional()\n .describe('Tool choice control'),\n thinking: z\n .object({ type: z.enum(['enabled', 'disabled']) })\n .optional()\n .describe('Enable thinking mode'),\n json_mode: z.boolean().optional().describe('Enable JSON output mode'),\n },\n {\n title: 'DeepSeek Chat',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n async (params) => {\n try {\n // Transparently convert reasoner to chat + thinking\n // deepseek-reasoner = deepseek-chat with thinking always enabled\n // chat + thinking supports function calling, which reasoner alone does not\n let model = params.model;\n let thinking = params.thinking;\n const isReasonerRouted = params.model === 'deepseek-reasoner';\n if (isReasonerRouted) {\n model = 'deepseek-chat';\n thinking = { type: 'enabled' as const };\n }\n\n const isThinkingMode = thinking?.type === 'enabled';\n\n // Build DeepSeek API request body — always stream to avoid CF timeout\n const body: Record = {\n model,\n messages: params.messages,\n stream: true,\n };\n // Sampling params are ignored by thinking mode - don't send them\n if (params.temperature !== undefined && !isThinkingMode) body.temperature = params.temperature;\n if (params.max_tokens !== undefined) body.max_tokens = params.max_tokens;\n if (params.tools) body.tools = params.tools;\n if (params.tool_choice) body.tool_choice = params.tool_choice;\n if (thinking) body.thinking = thinking;\n if (params.json_mode) body.response_format = { type: 'json_object' };\n\n const apiResponse = await fetch(DEEPSEEK_API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!apiResponse.ok) {\n const errText = await apiResponse.text();\n return {\n content: [{ type: 'text' as const, text: `DeepSeek API Error (${apiResponse.status}): ${errText}` }],\n isError: true,\n };\n }\n\n // Read SSE stream and accumulate chunks\n let content = '';\n let reasoningContent = '';\n let responseModel = '';\n let finishReason = '';\n let usage: Record = {};\n const toolCalls: Record = {};\n\n const reader = apiResponse.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue;\n const data = line.slice(6).trim();\n if (data === '[DONE]') continue;\n\n try {\n const chunk = JSON.parse(data);\n if (chunk.model) responseModel = chunk.model;\n if (chunk.usage) usage = chunk.usage;\n\n const delta = chunk.choices?.[0]?.delta;\n if (!delta) {\n if (chunk.choices?.[0]?.finish_reason) finishReason = chunk.choices[0].finish_reason;\n continue;\n }\n\n if (delta.content) content += delta.content;\n if (delta.reasoning_content) reasoningContent += delta.reasoning_content;\n if (delta.finish_reason) finishReason = delta.finish_reason;\n\n // Accumulate tool calls\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n if (!toolCalls[idx]) {\n toolCalls[idx] = { id: tc.id || '', type: 'function', function: { name: '', arguments: '' } };\n }\n if (tc.id) toolCalls[idx].id = tc.id;\n if (tc.function?.name) toolCalls[idx].function.name += tc.function.name;\n if (tc.function?.arguments) toolCalls[idx].function.arguments += tc.function.arguments;\n }\n }\n } catch {\n // skip malformed chunks\n }\n }\n }\n\n const toolCallsArray = Object.values(toolCalls);\n const cost = calculateCost(usage);\n\n // Format response text\n let text = '';\n if (reasoningContent) {\n text += `\\n${reasoningContent}\\n\\n\\n`;\n }\n text += content;\n\n if (toolCallsArray.length > 0) {\n text += '\\n\\n**Function Calls:**\\n';\n for (const tc of toolCallsArray) {\n text += `\\`${tc.function.name}\\`\\n`;\n text += `- Call ID: ${tc.id}\\n`;\n text += `- Arguments: ${tc.function.arguments}\\n\\n`;\n }\n }\n\n text += `\\n---\\n**Request Info:**\\n`;\n text += `- **Tokens:** ${usage.total_tokens || 0} (${usage.prompt_tokens || 0} prompt + ${usage.completion_tokens || 0} completion)\\n`;\n text += `- **Model:** ${responseModel || model}\\n`;\n text += `- **Cost:** ${cost.formatted}`;\n if (isReasonerRouted) {\n text += `\\n- **Routed:** reasoner -> chat + thinking`;\n }\n\n return {\n content: [{ type: 'text' as const, text }],\n structuredContent: {\n content,\n reasoning_content: reasoningContent || undefined,\n model: responseModel || model,\n usage,\n finish_reason: finishReason,\n tool_calls: toolCallsArray.length > 0 ? toolCallsArray : undefined,\n ...(isReasonerRouted ? { routed_from: 'deepseek-reasoner' } : {}),\n cost_usd: parseFloat(cost.totalCost.toFixed(6)),\n } as unknown as Record,\n };\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${msg}` }],\n isError: true,\n };\n }\n }\n );\n\n return server;\n}\n\nexport default {\n async fetch(request: Request, _env: Env): Promise {\n const url = new URL(request.url);\n\n // CORS preflight\n if (request.method === 'OPTIONS') {\n return new Response(null, {\n headers: {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization, Mcp-Session-Id',\n },\n });\n }\n\n // MCP Server Card — allows Smithery and other registries to discover capabilities without scanning\n if (url.pathname === '/.well-known/mcp/server-card.json') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek AI — chat, reasoning, function calling, JSON mode, cost tracking',\n transport: { type: 'streamable-http', url: 'https://deepseek-mcp.tahirl.com/mcp' },\n capabilities: { tools: true, prompts: false, resources: false },\n tools: [\n {\n name: 'deepseek_chat',\n description: 'Chat with DeepSeek AI models (Chat + Reasoner). Supports function calling, thinking mode, JSON output, cost tracking.',\n annotations: {\n title: 'DeepSeek Chat',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n inputSchema: {\n type: 'object',\n required: ['messages'],\n properties: {\n messages: { type: 'array', description: 'Conversation messages', items: { type: 'object' } },\n model: { type: 'string', enum: ['deepseek-chat', 'deepseek-reasoner'], default: 'deepseek-chat' },\n temperature: { type: 'number', description: 'Sampling temperature (0-2)' },\n max_tokens: { type: 'number', description: 'Max tokens to generate' },\n stream: { type: 'boolean', default: false },\n tools: { type: 'array', description: 'Tool definitions for function calling' },\n tool_choice: { description: 'Tool choice control' },\n thinking: { type: 'object', description: 'Enable thinking mode' },\n json_mode: { type: 'boolean', description: 'Enable JSON output mode' },\n },\n },\n },\n ],\n auth: { type: 'bearer', description: 'DeepSeek API key as Bearer token' },\n icon: 'https://raw.githubusercontent.com/arikusi/deepseek-mcp-server/main/icon.png',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // Health check\n if (url.pathname === '/health') {\n return Response.json({\n status: 'ok',\n version: VERSION,\n transport: 'cloudflare-workers',\n timestamp: new Date().toISOString(),\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // Root — info page\n if (url.pathname === '/' && request.method === 'GET') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek AI models — BYOK (Bring Your Own Key)',\n endpoints: { mcp: '/mcp', health: '/health' },\n auth: 'Send your DeepSeek API key as Authorization: Bearer ',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // MCP endpoint\n if (url.pathname === '/mcp') {\n // Stateless mode: only POST is supported (no SSE via GET/DELETE)\n if (request.method !== 'POST') {\n return Response.json(\n { error: 'Method not allowed. Use POST for MCP requests.' },\n { status: 405, headers: { 'Access-Control-Allow-Origin': '*' } }\n );\n }\n\n // BYOK: require API key\n const apiKey = extractApiKey(request);\n if (!apiKey) {\n return Response.json(\n { error: 'Authorization required. Send your DeepSeek API key as: Authorization: Bearer ' },\n { status: 401, headers: { 'Access-Control-Allow-Origin': '*' } }\n );\n }\n\n // Stateless: create fresh server + transport per request\n const server = createMcpServer(apiKey);\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse: true,\n });\n await server.connect(transport);\n\n const response = await transport.handleRequest(request);\n\n // Add CORS headers\n const headers = new Headers(response.headers);\n headers.set('Access-Control-Allow-Origin', '*');\n headers.set('Access-Control-Expose-Headers', 'Mcp-Session-Id');\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n return Response.json({ error: 'Not Found' }, { status: 404 });\n },\n};\n"}, "2ca34c8f8e794b091a51ed1c300ea307c6087f79": {"t": 1785749506.951029, "n": 14674, "h": "e23208b05e0dc4fb", "c": "/**\n * DeepSeek MCP Server — Cloudflare Worker\n * Stateless Streamable HTTP transport with BYOK (Bring Your Own Key)\n *\n * Users send their own DeepSeek API key as Bearer token.\n * The worker proxies MCP requests to DeepSeek API.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';\nimport { z } from 'zod';\n\nconst VERSION = '1.6.0';\nconst DEEPSEEK_API_URL = 'https://api.deepseek.com/chat/completions';\n\n// Pricing per 1M tokens (DeepSeek V3.2 unified)\nconst PRICING = {\n cacheHit: 0.028,\n cacheMiss: 0.28,\n output: 0.42,\n};\n\ninterface Env {}\n\nfunction extractApiKey(request: Request): string | null {\n const auth = request.headers.get('authorization');\n if (!auth?.startsWith('Bearer ')) return null;\n return auth.slice(7);\n}\n\nfunction calculateCost(usage: Record): { totalCost: number; formatted: string } {\n const cacheHit = usage.prompt_cache_hit_tokens || 0;\n const cacheMiss = usage.prompt_cache_miss_tokens || usage.prompt_tokens || 0;\n const output = usage.completion_tokens || 0;\n\n const totalCost =\n (cacheHit * PRICING.cacheHit) / 1_000_000 +\n (cacheMiss * PRICING.cacheMiss) / 1_000_000 +\n (output * PRICING.output) / 1_000_000;\n\n let formatted = `$${totalCost.toFixed(6)}`;\n if (cacheHit > 0) {\n const ratio = ((cacheHit / (cacheHit + cacheMiss)) * 100).toFixed(0);\n formatted += ` (cache hit: ${ratio}%)`;\n }\n return { totalCost, formatted };\n}\n\nfunction createMcpServer(apiKey: string): McpServer {\n const server = new McpServer({ name: 'deepseek-mcp-server', version: VERSION });\n\n server.tool(\n 'deepseek_chat',\n 'Chat with DeepSeek AI models (Chat + Reasoner). Supports function calling, thinking mode, JSON output, cost tracking.',\n {\n messages: z\n .array(\n z.object({\n role: z.enum(['system', 'user', 'assistant', 'tool']),\n content: z.string(),\n tool_call_id: z.string().optional(),\n })\n )\n .min(1)\n .describe('Conversation messages'),\n model: z\n .enum(['deepseek-chat', 'deepseek-reasoner'])\n .default('deepseek-chat')\n .describe('Model to use'),\n temperature: z.number().min(0).max(2).optional().describe('Sampling temperature (0-2)'),\n max_tokens: z.number().min(1).max(65536).optional().describe('Max tokens to generate'),\n stream: z.boolean().default(false).describe('Enable streaming (accumulated response returned)'),\n tools: z\n .array(\n z.object({\n type: z.literal('function'),\n function: z.object({\n name: z.string().min(1),\n description: z.string().optional(),\n parameters: z.record(z.string(), z.unknown()).optional(),\n }),\n })\n )\n .max(128)\n .optional()\n .describe('Tool definitions for function calling'),\n tool_choice: z\n .union([\n z.enum(['auto', 'none', 'required']),\n z.object({\n type: z.literal('function'),\n function: z.object({ name: z.string().min(1) }),\n }),\n ])\n .optional()\n .describe('Tool choice control'),\n thinking: z\n .object({ type: z.enum(['enabled', 'disabled']) })\n .optional()\n .describe('Enable thinking mode'),\n json_mode: z.boolean().optional().describe('Enable JSON output mode'),\n },\n {\n title: 'DeepSeek Chat',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n async (params) => {\n try {\n // Transparently convert reasoner to chat + thinking\n // deepseek-reasoner = deepseek-chat with thinking always enabled\n // chat + thinking supports function calling, which reasoner alone does not\n let model = params.model;\n let thinking = params.thinking;\n const isReasonerRouted = params.model === 'deepseek-reasoner';\n if (isReasonerRouted) {\n model = 'deepseek-chat';\n thinking = { type: 'enabled' as const };\n }\n\n const isThinkingMode = thinking?.type === 'enabled';\n\n // Build DeepSeek API request body — always stream to avoid CF timeout\n const body: Record = {\n model,\n messages: params.messages,\n stream: true,\n };\n // Sampling params are ignored by thinking mode - don't send them\n if (params.temperature !== undefined && !isThinkingMode) body.temperature = params.temperature;\n if (params.max_tokens !== undefined) body.max_tokens = params.max_tokens;\n if (params.tools) body.tools = params.tools;\n if (params.tool_choice) body.tool_choice = params.tool_choice;\n if (thinking) body.thinking = thinking;\n if (params.json_mode) body.response_format = { type: 'json_object' };\n\n const apiResponse = await fetch(DEEPSEEK_API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!apiResponse.ok) {\n const errText = await apiResponse.text();\n return {\n content: [{ type: 'text' as const, text: `DeepSeek API Error (${apiResponse.status}): ${errText}` }],\n isError: true,\n };\n }\n\n // Read SSE stream and accumulate chunks\n let content = '';\n let reasoningContent = '';\n let model = '';\n let finishReason = '';\n let usage: Record = {};\n const toolCalls: Record = {};\n\n const reader = apiResponse.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue;\n const data = line.slice(6).trim();\n if (data === '[DONE]') continue;\n\n try {\n const chunk = JSON.parse(data);\n if (chunk.model) model = chunk.model;\n if (chunk.usage) usage = chunk.usage;\n\n const delta = chunk.choices?.[0]?.delta;\n if (!delta) {\n if (chunk.choices?.[0]?.finish_reason) finishReason = chunk.choices[0].finish_reason;\n continue;\n }\n\n if (delta.content) content += delta.content;\n if (delta.reasoning_content) reasoningContent += delta.reasoning_content;\n if (delta.finish_reason) finishReason = delta.finish_reason;\n\n // Accumulate tool calls\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n if (!toolCalls[idx]) {\n toolCalls[idx] = { id: tc.id || '', type: 'function', function: { name: '', arguments: '' } };\n }\n if (tc.id) toolCalls[idx].id = tc.id;\n if (tc.function?.name) toolCalls[idx].function.name += tc.function.name;\n if (tc.function?.arguments) toolCalls[idx].function.arguments += tc.function.arguments;\n }\n }\n } catch {\n // skip malformed chunks\n }\n }\n }\n\n const toolCallsArray = Object.values(toolCalls);\n const cost = calculateCost(usage);\n\n // Format response text\n let text = '';\n if (reasoningContent) {\n text += `\\n${reasoningContent}\\n\\n\\n`;\n }\n text += content;\n\n if (toolCallsArray.length > 0) {\n text += '\\n\\n**Function Calls:**\\n';\n for (const tc of toolCallsArray) {\n text += `\\`${tc.function.name}\\`\\n`;\n text += `- Call ID: ${tc.id}\\n`;\n text += `- Arguments: ${tc.function.arguments}\\n\\n`;\n }\n }\n\n text += `\\n---\\n**Request Info:**\\n`;\n text += `- **Tokens:** ${usage.total_tokens || 0} (${usage.prompt_tokens || 0} prompt + ${usage.completion_tokens || 0} completion)\\n`;\n text += `- **Model:** ${model}\\n`;\n text += `- **Cost:** ${cost.formatted}`;\n if (isReasonerRouted) {\n text += `\\n- **Routed:** reasoner -> chat + thinking`;\n }\n\n return {\n content: [{ type: 'text' as const, text }],\n structuredContent: {\n content,\n reasoning_content: reasoningContent || undefined,\n model,\n usage,\n finish_reason: finishReason,\n tool_calls: toolCallsArray.length > 0 ? toolCallsArray : undefined,\n ...(isReasonerRouted ? { routed_from: 'deepseek-reasoner' } : {}),\n cost_usd: parseFloat(cost.totalCost.toFixed(6)),\n } as unknown as Record,\n };\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${msg}` }],\n isError: true,\n };\n }\n }\n );\n\n return server;\n}\n\nexport default {\n async fetch(request: Request, _env: Env): Promise {\n const url = new URL(request.url);\n\n // CORS preflight\n if (request.method === 'OPTIONS') {\n return new Response(null, {\n headers: {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization, Mcp-Session-Id',\n },\n });\n }\n\n // MCP Server Card — allows Smithery and other registries to discover capabilities without scanning\n if (url.pathname === '/.well-known/mcp/server-card.json') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek AI — chat, reasoning, function calling, JSON mode, cost tracking',\n transport: { type: 'streamable-http', url: 'https://deepseek-mcp.tahirl.com/mcp' },\n capabilities: { tools: true, prompts: false, resources: false },\n tools: [\n {\n name: 'deepseek_chat',\n description: 'Chat with DeepSeek AI models (Chat + Reasoner). Supports function calling, thinking mode, JSON output, cost tracking.',\n annotations: {\n title: 'DeepSeek Chat',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n inputSchema: {\n type: 'object',\n required: ['messages'],\n properties: {\n messages: { type: 'array', description: 'Conversation messages', items: { type: 'object' } },\n model: { type: 'string', enum: ['deepseek-chat', 'deepseek-reasoner'], default: 'deepseek-chat' },\n temperature: { type: 'number', description: 'Sampling temperature (0-2)' },\n max_tokens: { type: 'number', description: 'Max tokens to generate' },\n stream: { type: 'boolean', default: false },\n tools: { type: 'array', description: 'Tool definitions for function calling' },\n tool_choice: { description: 'Tool choice control' },\n thinking: { type: 'object', description: 'Enable thinking mode' },\n json_mode: { type: 'boolean', description: 'Enable JSON output mode' },\n },\n },\n },\n ],\n auth: { type: 'bearer', description: 'DeepSeek API key as Bearer token' },\n icon: 'https://raw.githubusercontent.com/arikusi/deepseek-mcp-server/main/icon.png',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // Health check\n if (url.pathname === '/health') {\n return Response.json({\n status: 'ok',\n version: VERSION,\n transport: 'cloudflare-workers',\n timestamp: new Date().toISOString(),\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // Root — info page\n if (url.pathname === '/' && request.method === 'GET') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek AI models — BYOK (Bring Your Own Key)',\n endpoints: { mcp: '/mcp', health: '/health' },\n auth: 'Send your DeepSeek API key as Authorization: Bearer ',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // MCP endpoint\n if (url.pathname === '/mcp') {\n // Stateless mode: only POST is supported (no SSE via GET/DELETE)\n if (request.method !== 'POST') {\n return Response.json(\n { error: 'Method not allowed. Use POST for MCP requests.' },\n { status: 405, headers: { 'Access-Control-Allow-Origin': '*' } }\n );\n }\n\n // BYOK: require API key\n const apiKey = extractApiKey(request);\n if (!apiKey) {\n return Response.json(\n { error: 'Authorization required. Send your DeepSeek API key as: Authorization: Bearer ' },\n { status: 401, headers: { 'Access-Control-Allow-Origin': '*' } }\n );\n }\n\n // Stateless: create fresh server + transport per request\n const server = createMcpServer(apiKey);\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse: true,\n });\n await server.connect(transport);\n\n const response = await transport.handleRequest(request);\n\n // Add CORS headers\n const headers = new Headers(response.headers);\n headers.set('Access-Control-Allow-Origin', '*');\n headers.set('Access-Control-Expose-Headers', 'Mcp-Session-Id');\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n return Response.json({ error: 'Not Found' }, { status: 404 });\n },\n};\n"}, "178e63a392f3e73663085bd2aa30f3c12d61189f": {"t": 1785749508.2265801, "n": 13868, "h": "fccae88f02f23e12", "c": "/**\n * DeepSeek MCP Server — Cloudflare Worker\n * Stateless Streamable HTTP transport with BYOK (Bring Your Own Key)\n *\n * Users send their own DeepSeek API key as Bearer token.\n * The worker proxies MCP requests to DeepSeek API.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';\nimport { z } from 'zod';\n\nconst VERSION = '1.5.2';\nconst DEEPSEEK_API_URL = 'https://api.deepseek.com/chat/completions';\n\n// Pricing per 1M tokens (DeepSeek V3.2 unified)\nconst PRICING = {\n cacheHit: 0.028,\n cacheMiss: 0.28,\n output: 0.42,\n};\n\ninterface Env {}\n\nfunction extractApiKey(request: Request): string | null {\n const auth = request.headers.get('authorization');\n if (!auth?.startsWith('Bearer ')) return null;\n return auth.slice(7);\n}\n\nfunction calculateCost(usage: Record): { totalCost: number; formatted: string } {\n const cacheHit = usage.prompt_cache_hit_tokens || 0;\n const cacheMiss = usage.prompt_cache_miss_tokens || usage.prompt_tokens || 0;\n const output = usage.completion_tokens || 0;\n\n const totalCost =\n (cacheHit * PRICING.cacheHit) / 1_000_000 +\n (cacheMiss * PRICING.cacheMiss) / 1_000_000 +\n (output * PRICING.output) / 1_000_000;\n\n let formatted = `$${totalCost.toFixed(6)}`;\n if (cacheHit > 0) {\n const ratio = ((cacheHit / (cacheHit + cacheMiss)) * 100).toFixed(0);\n formatted += ` (cache hit: ${ratio}%)`;\n }\n return { totalCost, formatted };\n}\n\nfunction createMcpServer(apiKey: string): McpServer {\n const server = new McpServer({ name: 'deepseek-mcp-server', version: VERSION });\n\n server.tool(\n 'deepseek_chat',\n 'Chat with DeepSeek AI models (Chat + Reasoner). Supports function calling, thinking mode, JSON output, cost tracking.',\n {\n messages: z\n .array(\n z.object({\n role: z.enum(['system', 'user', 'assistant', 'tool']),\n content: z.string(),\n tool_call_id: z.string().optional(),\n })\n )\n .min(1)\n .describe('Conversation messages'),\n model: z\n .enum(['deepseek-chat', 'deepseek-reasoner'])\n .default('deepseek-chat')\n .describe('Model to use'),\n temperature: z.number().min(0).max(2).optional().describe('Sampling temperature (0-2)'),\n max_tokens: z.number().min(1).max(65536).optional().describe('Max tokens to generate'),\n stream: z.boolean().default(false).describe('Enable streaming (accumulated response returned)'),\n tools: z\n .array(\n z.object({\n type: z.literal('function'),\n function: z.object({\n name: z.string().min(1),\n description: z.string().optional(),\n parameters: z.record(z.string(), z.unknown()).optional(),\n }),\n })\n )\n .max(128)\n .optional()\n .describe('Tool definitions for function calling'),\n tool_choice: z\n .union([\n z.enum(['auto', 'none', 'required']),\n z.object({\n type: z.literal('function'),\n function: z.object({ name: z.string().min(1) }),\n }),\n ])\n .optional()\n .describe('Tool choice control'),\n thinking: z\n .object({ type: z.enum(['enabled', 'disabled']) })\n .optional()\n .describe('Enable thinking mode'),\n json_mode: z.boolean().optional().describe('Enable JSON output mode'),\n },\n {\n title: 'DeepSeek Chat',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n async (params) => {\n try {\n // Build DeepSeek API request body — always stream to avoid CF timeout\n const body: Record = {\n model: params.model,\n messages: params.messages,\n stream: true,\n };\n if (params.temperature !== undefined) body.temperature = params.temperature;\n if (params.max_tokens !== undefined) body.max_tokens = params.max_tokens;\n if (params.tools) body.tools = params.tools;\n if (params.tool_choice) body.tool_choice = params.tool_choice;\n if (params.thinking) body.thinking = params.thinking;\n if (params.json_mode) body.response_format = { type: 'json_object' };\n\n const apiResponse = await fetch(DEEPSEEK_API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!apiResponse.ok) {\n const errText = await apiResponse.text();\n return {\n content: [{ type: 'text' as const, text: `DeepSeek API Error (${apiResponse.status}): ${errText}` }],\n isError: true,\n };\n }\n\n // Read SSE stream and accumulate chunks\n let content = '';\n let reasoningContent = '';\n let model = '';\n let finishReason = '';\n let usage: Record = {};\n const toolCalls: Record = {};\n\n const reader = apiResponse.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue;\n const data = line.slice(6).trim();\n if (data === '[DONE]') continue;\n\n try {\n const chunk = JSON.parse(data);\n if (chunk.model) model = chunk.model;\n if (chunk.usage) usage = chunk.usage;\n\n const delta = chunk.choices?.[0]?.delta;\n if (!delta) {\n if (chunk.choices?.[0]?.finish_reason) finishReason = chunk.choices[0].finish_reason;\n continue;\n }\n\n if (delta.content) content += delta.content;\n if (delta.reasoning_content) reasoningContent += delta.reasoning_content;\n if (delta.finish_reason) finishReason = delta.finish_reason;\n\n // Accumulate tool calls\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n if (!toolCalls[idx]) {\n toolCalls[idx] = { id: tc.id || '', type: 'function', function: { name: '', arguments: '' } };\n }\n if (tc.id) toolCalls[idx].id = tc.id;\n if (tc.function?.name) toolCalls[idx].function.name += tc.function.name;\n if (tc.function?.arguments) toolCalls[idx].function.arguments += tc.function.arguments;\n }\n }\n } catch {\n // skip malformed chunks\n }\n }\n }\n\n const toolCallsArray = Object.values(toolCalls);\n const cost = calculateCost(usage);\n\n // Format response text\n let text = '';\n if (reasoningContent) {\n text += `\\n${reasoningContent}\\n\\n\\n`;\n }\n text += content;\n\n if (toolCallsArray.length > 0) {\n text += '\\n\\n**Function Calls:**\\n';\n for (const tc of toolCallsArray) {\n text += `\\`${tc.function.name}\\`\\n`;\n text += `- Call ID: ${tc.id}\\n`;\n text += `- Arguments: ${tc.function.arguments}\\n\\n`;\n }\n }\n\n text += `\\n---\\n**Request Info:**\\n`;\n text += `- **Tokens:** ${usage.total_tokens || 0} (${usage.prompt_tokens || 0} prompt + ${usage.completion_tokens || 0} completion)\\n`;\n text += `- **Model:** ${model}\\n`;\n text += `- **Cost:** ${cost.formatted}`;\n\n return {\n content: [{ type: 'text' as const, text }],\n structuredContent: {\n content,\n reasoning_content: reasoningContent || undefined,\n model,\n usage,\n finish_reason: finishReason,\n tool_calls: toolCallsArray.length > 0 ? toolCallsArray : undefined,\n cost_usd: parseFloat(cost.totalCost.toFixed(6)),\n } as unknown as Record,\n };\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${msg}` }],\n isError: true,\n };\n }\n }\n );\n\n return server;\n}\n\nexport default {\n async fetch(request: Request, _env: Env): Promise {\n const url = new URL(request.url);\n\n // CORS preflight\n if (request.method === 'OPTIONS') {\n return new Response(null, {\n headers: {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization, Mcp-Session-Id',\n },\n });\n }\n\n // MCP Server Card — allows Smithery and other registries to discover capabilities without scanning\n if (url.pathname === '/.well-known/mcp/server-card.json') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek AI — chat, reasoning, function calling, JSON mode, cost tracking',\n transport: { type: 'streamable-http', url: 'https://deepseek-mcp.tahirl.com/mcp' },\n capabilities: { tools: true, prompts: false, resources: false },\n tools: [\n {\n name: 'deepseek_chat',\n description: 'Chat with DeepSeek AI models (Chat + Reasoner). Supports function calling, thinking mode, JSON output, cost tracking.',\n annotations: {\n title: 'DeepSeek Chat',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n inputSchema: {\n type: 'object',\n required: ['messages'],\n properties: {\n messages: { type: 'array', description: 'Conversation messages', items: { type: 'object' } },\n model: { type: 'string', enum: ['deepseek-chat', 'deepseek-reasoner'], default: 'deepseek-chat' },\n temperature: { type: 'number', description: 'Sampling temperature (0-2)' },\n max_tokens: { type: 'number', description: 'Max tokens to generate' },\n stream: { type: 'boolean', default: false },\n tools: { type: 'array', description: 'Tool definitions for function calling' },\n tool_choice: { description: 'Tool choice control' },\n thinking: { type: 'object', description: 'Enable thinking mode' },\n json_mode: { type: 'boolean', description: 'Enable JSON output mode' },\n },\n },\n },\n ],\n auth: { type: 'bearer', description: 'DeepSeek API key as Bearer token' },\n icon: 'https://raw.githubusercontent.com/arikusi/deepseek-mcp-server/main/icon.png',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // Health check\n if (url.pathname === '/health') {\n return Response.json({\n status: 'ok',\n version: VERSION,\n transport: 'cloudflare-workers',\n timestamp: new Date().toISOString(),\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // Root — info page\n if (url.pathname === '/' && request.method === 'GET') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek AI models — BYOK (Bring Your Own Key)',\n endpoints: { mcp: '/mcp', health: '/health' },\n auth: 'Send your DeepSeek API key as Authorization: Bearer ',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // MCP endpoint\n if (url.pathname === '/mcp') {\n // Stateless mode: only POST is supported (no SSE via GET/DELETE)\n if (request.method !== 'POST') {\n return Response.json(\n { error: 'Method not allowed. Use POST for MCP requests.' },\n { status: 405, headers: { 'Access-Control-Allow-Origin': '*' } }\n );\n }\n\n // BYOK: require API key\n const apiKey = extractApiKey(request);\n if (!apiKey) {\n return Response.json(\n { error: 'Authorization required. Send your DeepSeek API key as: Authorization: Bearer ' },\n { status: 401, headers: { 'Access-Control-Allow-Origin': '*' } }\n );\n }\n\n // Stateless: create fresh server + transport per request\n const server = createMcpServer(apiKey);\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse: true,\n });\n await server.connect(transport);\n\n const response = await transport.handleRequest(request);\n\n // Add CORS headers\n const headers = new Headers(response.headers);\n headers.set('Access-Control-Allow-Origin', '*');\n headers.set('Access-Control-Expose-Headers', 'Mcp-Session-Id');\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n return Response.json({ error: 'Not Found' }, { status: 404 });\n },\n};\n"}, "87a6d1ab40360c72d850867da018adf0d9758f17": {"t": 1785749509.347722, "n": 13512, "h": "786bac01ec99755a", "c": "/**\n * DeepSeek MCP Server — Cloudflare Worker\n * Stateless Streamable HTTP transport with BYOK (Bring Your Own Key)\n *\n * Users send their own DeepSeek API key as Bearer token.\n * The worker proxies MCP requests to DeepSeek API.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';\nimport { z } from 'zod';\n\nconst VERSION = '1.5.0';\nconst DEEPSEEK_API_URL = 'https://api.deepseek.com/chat/completions';\n\n// Pricing per 1M tokens (DeepSeek V3.2 unified)\nconst PRICING = {\n cacheHit: 0.028,\n cacheMiss: 0.28,\n output: 0.42,\n};\n\ninterface Env {}\n\nfunction extractApiKey(request: Request): string | null {\n const auth = request.headers.get('authorization');\n if (!auth?.startsWith('Bearer ')) return null;\n return auth.slice(7);\n}\n\nfunction calculateCost(usage: Record): { totalCost: number; formatted: string } {\n const cacheHit = usage.prompt_cache_hit_tokens || 0;\n const cacheMiss = usage.prompt_cache_miss_tokens || usage.prompt_tokens || 0;\n const output = usage.completion_tokens || 0;\n\n const totalCost =\n (cacheHit * PRICING.cacheHit) / 1_000_000 +\n (cacheMiss * PRICING.cacheMiss) / 1_000_000 +\n (output * PRICING.output) / 1_000_000;\n\n let formatted = `$${totalCost.toFixed(6)}`;\n if (cacheHit > 0) {\n const ratio = ((cacheHit / (cacheHit + cacheMiss)) * 100).toFixed(0);\n formatted += ` (cache hit: ${ratio}%)`;\n }\n return { totalCost, formatted };\n}\n\nfunction createMcpServer(apiKey: string): McpServer {\n const server = new McpServer({ name: 'deepseek-mcp-server', version: VERSION });\n\n server.tool(\n 'deepseek_chat',\n 'Chat with DeepSeek AI models (Chat + Reasoner). Supports function calling, thinking mode, JSON output, cost tracking.',\n {\n messages: z\n .array(\n z.object({\n role: z.enum(['system', 'user', 'assistant', 'tool']),\n content: z.string(),\n tool_call_id: z.string().optional(),\n })\n )\n .min(1)\n .describe('Conversation messages'),\n model: z\n .enum(['deepseek-chat', 'deepseek-reasoner'])\n .default('deepseek-chat')\n .describe('Model to use'),\n temperature: z.number().min(0).max(2).optional().describe('Sampling temperature (0-2)'),\n max_tokens: z.number().min(1).max(65536).optional().describe('Max tokens to generate'),\n stream: z.boolean().default(false).describe('Enable streaming (accumulated response returned)'),\n tools: z\n .array(\n z.object({\n type: z.literal('function'),\n function: z.object({\n name: z.string().min(1),\n description: z.string().optional(),\n parameters: z.record(z.string(), z.unknown()).optional(),\n }),\n })\n )\n .max(128)\n .optional()\n .describe('Tool definitions for function calling'),\n tool_choice: z\n .union([\n z.enum(['auto', 'none', 'required']),\n z.object({\n type: z.literal('function'),\n function: z.object({ name: z.string().min(1) }),\n }),\n ])\n .optional()\n .describe('Tool choice control'),\n thinking: z\n .object({ type: z.enum(['enabled', 'disabled']) })\n .optional()\n .describe('Enable thinking mode'),\n json_mode: z.boolean().optional().describe('Enable JSON output mode'),\n },\n {\n title: 'DeepSeek Chat',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n async (params) => {\n try {\n // Build DeepSeek API request body — always stream to avoid CF timeout\n const body: Record = {\n model: params.model,\n messages: params.messages,\n stream: true,\n };\n if (params.temperature !== undefined) body.temperature = params.temperature;\n if (params.max_tokens !== undefined) body.max_tokens = params.max_tokens;\n if (params.tools) body.tools = params.tools;\n if (params.tool_choice) body.tool_choice = params.tool_choice;\n if (params.thinking) body.thinking = params.thinking;\n if (params.json_mode) body.response_format = { type: 'json_object' };\n\n const apiResponse = await fetch(DEEPSEEK_API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!apiResponse.ok) {\n const errText = await apiResponse.text();\n return {\n content: [{ type: 'text' as const, text: `DeepSeek API Error (${apiResponse.status}): ${errText}` }],\n isError: true,\n };\n }\n\n // Read SSE stream and accumulate chunks\n let content = '';\n let reasoningContent = '';\n let model = '';\n let finishReason = '';\n let usage: Record = {};\n const toolCalls: Record = {};\n\n const reader = apiResponse.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue;\n const data = line.slice(6).trim();\n if (data === '[DONE]') continue;\n\n try {\n const chunk = JSON.parse(data);\n if (chunk.model) model = chunk.model;\n if (chunk.usage) usage = chunk.usage;\n\n const delta = chunk.choices?.[0]?.delta;\n if (!delta) {\n if (chunk.choices?.[0]?.finish_reason) finishReason = chunk.choices[0].finish_reason;\n continue;\n }\n\n if (delta.content) content += delta.content;\n if (delta.reasoning_content) reasoningContent += delta.reasoning_content;\n if (delta.finish_reason) finishReason = delta.finish_reason;\n\n // Accumulate tool calls\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n if (!toolCalls[idx]) {\n toolCalls[idx] = { id: tc.id || '', type: 'function', function: { name: '', arguments: '' } };\n }\n if (tc.id) toolCalls[idx].id = tc.id;\n if (tc.function?.name) toolCalls[idx].function.name += tc.function.name;\n if (tc.function?.arguments) toolCalls[idx].function.arguments += tc.function.arguments;\n }\n }\n } catch {\n // skip malformed chunks\n }\n }\n }\n\n const toolCallsArray = Object.values(toolCalls);\n const cost = calculateCost(usage);\n\n // Format response text\n let text = '';\n if (reasoningContent) {\n text += `\\n${reasoningContent}\\n\\n\\n`;\n }\n text += content;\n\n if (toolCallsArray.length > 0) {\n text += '\\n\\n**Function Calls:**\\n';\n for (const tc of toolCallsArray) {\n text += `\\`${tc.function.name}\\`\\n`;\n text += `- Call ID: ${tc.id}\\n`;\n text += `- Arguments: ${tc.function.arguments}\\n\\n`;\n }\n }\n\n text += `\\n---\\n**Request Info:**\\n`;\n text += `- **Tokens:** ${usage.total_tokens || 0} (${usage.prompt_tokens || 0} prompt + ${usage.completion_tokens || 0} completion)\\n`;\n text += `- **Model:** ${model}\\n`;\n text += `- **Cost:** ${cost.formatted}`;\n\n return {\n content: [{ type: 'text' as const, text }],\n structuredContent: {\n content,\n reasoning_content: reasoningContent || undefined,\n model,\n usage,\n finish_reason: finishReason,\n tool_calls: toolCallsArray.length > 0 ? toolCallsArray : undefined,\n cost_usd: parseFloat(cost.totalCost.toFixed(6)),\n } as unknown as Record,\n };\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${msg}` }],\n isError: true,\n };\n }\n }\n );\n\n return server;\n}\n\nexport default {\n async fetch(request: Request, _env: Env): Promise {\n const url = new URL(request.url);\n\n // CORS preflight\n if (request.method === 'OPTIONS') {\n return new Response(null, {\n headers: {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization, Mcp-Session-Id',\n },\n });\n }\n\n // MCP Server Card — allows Smithery and other registries to discover capabilities without scanning\n if (url.pathname === '/.well-known/mcp/server-card.json') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek AI — chat, reasoning, function calling, JSON mode, cost tracking',\n transport: { type: 'streamable-http', url: 'https://deepseek-mcp.tahirl.com/mcp' },\n capabilities: { tools: true, prompts: false, resources: false },\n tools: [\n {\n name: 'deepseek_chat',\n description: 'Chat with DeepSeek AI models (Chat + Reasoner). Supports function calling, thinking mode, JSON output, cost tracking.',\n annotations: {\n title: 'DeepSeek Chat',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n inputSchema: {\n type: 'object',\n required: ['messages'],\n properties: {\n messages: { type: 'array', description: 'Conversation messages', items: { type: 'object' } },\n model: { type: 'string', enum: ['deepseek-chat', 'deepseek-reasoner'], default: 'deepseek-chat' },\n temperature: { type: 'number', description: 'Sampling temperature (0-2)' },\n max_tokens: { type: 'number', description: 'Max tokens to generate' },\n stream: { type: 'boolean', default: false },\n tools: { type: 'array', description: 'Tool definitions for function calling' },\n tool_choice: { description: 'Tool choice control' },\n thinking: { type: 'object', description: 'Enable thinking mode' },\n json_mode: { type: 'boolean', description: 'Enable JSON output mode' },\n },\n },\n },\n ],\n auth: { type: 'bearer', description: 'DeepSeek API key as Bearer token' },\n icon: 'https://raw.githubusercontent.com/arikusi/deepseek-mcp-server/main/icon.png',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // Health check\n if (url.pathname === '/health') {\n return Response.json({\n status: 'ok',\n version: VERSION,\n transport: 'cloudflare-workers',\n timestamp: new Date().toISOString(),\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // Root — info page\n if (url.pathname === '/' && request.method === 'GET') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek AI models — BYOK (Bring Your Own Key)',\n endpoints: { mcp: '/mcp', health: '/health' },\n auth: 'Send your DeepSeek API key as Authorization: Bearer ',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n }, {\n headers: { 'Access-Control-Allow-Origin': '*' },\n });\n }\n\n // MCP endpoint\n if (url.pathname === '/mcp') {\n // BYOK: require API key\n const apiKey = extractApiKey(request);\n if (!apiKey) {\n return Response.json(\n { error: 'Authorization required. Send your DeepSeek API key as: Authorization: Bearer ' },\n { status: 401 }\n );\n }\n\n // Stateless: create fresh server + transport per request\n const server = createMcpServer(apiKey);\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse: true,\n });\n await server.connect(transport);\n\n const response = await transport.handleRequest(request);\n\n // Add CORS headers\n const headers = new Headers(response.headers);\n headers.set('Access-Control-Allow-Origin', '*');\n headers.set('Access-Control-Expose-Headers', 'Mcp-Session-Id');\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n return Response.json({ error: 'Not Found' }, { status: 404 });\n },\n};\n"}, "d6f9b4ff31d863dcc7ae52158967038d1aefc816": {"t": 1785749510.4956052, "n": 13308, "h": "11c5081a0cc6e5e8", "c": "/**\n * DeepSeek MCP Server — Cloudflare Worker\n * Stateless Streamable HTTP transport with BYOK (Bring Your Own Key)\n *\n * Users send their own DeepSeek API key as Bearer token.\n * The worker proxies MCP requests to DeepSeek API.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';\nimport { z } from 'zod';\n\nconst VERSION = '1.5.0';\nconst DEEPSEEK_API_URL = 'https://api.deepseek.com/chat/completions';\n\n// Pricing per 1M tokens (DeepSeek V3.2 unified)\nconst PRICING = {\n cacheHit: 0.028,\n cacheMiss: 0.28,\n output: 0.42,\n};\n\ninterface Env {}\n\nfunction extractApiKey(request: Request): string | null {\n const auth = request.headers.get('authorization');\n if (!auth?.startsWith('Bearer ')) return null;\n return auth.slice(7);\n}\n\nfunction calculateCost(usage: Record): { totalCost: number; formatted: string } {\n const cacheHit = usage.prompt_cache_hit_tokens || 0;\n const cacheMiss = usage.prompt_cache_miss_tokens || usage.prompt_tokens || 0;\n const output = usage.completion_tokens || 0;\n\n const totalCost =\n (cacheHit * PRICING.cacheHit) / 1_000_000 +\n (cacheMiss * PRICING.cacheMiss) / 1_000_000 +\n (output * PRICING.output) / 1_000_000;\n\n let formatted = `$${totalCost.toFixed(6)}`;\n if (cacheHit > 0) {\n const ratio = ((cacheHit / (cacheHit + cacheMiss)) * 100).toFixed(0);\n formatted += ` (cache hit: ${ratio}%)`;\n }\n return { totalCost, formatted };\n}\n\nfunction createMcpServer(apiKey: string): McpServer {\n const server = new McpServer({ name: 'deepseek-mcp-server', version: VERSION });\n\n server.tool(\n 'deepseek_chat',\n 'Chat with DeepSeek AI models (Chat + Reasoner). Supports function calling, thinking mode, JSON output, cost tracking.',\n {\n messages: z\n .array(\n z.object({\n role: z.enum(['system', 'user', 'assistant', 'tool']),\n content: z.string(),\n tool_call_id: z.string().optional(),\n })\n )\n .min(1)\n .describe('Conversation messages'),\n model: z\n .enum(['deepseek-chat', 'deepseek-reasoner'])\n .default('deepseek-chat')\n .describe('Model to use'),\n temperature: z.number().min(0).max(2).optional().describe('Sampling temperature (0-2)'),\n max_tokens: z.number().min(1).max(65536).optional().describe('Max tokens to generate'),\n stream: z.boolean().default(false).describe('Enable streaming (accumulated response returned)'),\n tools: z\n .array(\n z.object({\n type: z.literal('function'),\n function: z.object({\n name: z.string().min(1),\n description: z.string().optional(),\n parameters: z.record(z.string(), z.unknown()).optional(),\n }),\n })\n )\n .max(128)\n .optional()\n .describe('Tool definitions for function calling'),\n tool_choice: z\n .union([\n z.enum(['auto', 'none', 'required']),\n z.object({\n type: z.literal('function'),\n function: z.object({ name: z.string().min(1) }),\n }),\n ])\n .optional()\n .describe('Tool choice control'),\n thinking: z\n .object({ type: z.enum(['enabled', 'disabled']) })\n .optional()\n .describe('Enable thinking mode'),\n json_mode: z.boolean().optional().describe('Enable JSON output mode'),\n },\n {\n title: 'DeepSeek Chat',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n async (params) => {\n try {\n // Build DeepSeek API request body — always stream to avoid CF timeout\n const body: Record = {\n model: params.model,\n messages: params.messages,\n stream: true,\n };\n if (params.temperature !== undefined) body.temperature = params.temperature;\n if (params.max_tokens !== undefined) body.max_tokens = params.max_tokens;\n if (params.tools) body.tools = params.tools;\n if (params.tool_choice) body.tool_choice = params.tool_choice;\n if (params.thinking) body.thinking = params.thinking;\n if (params.json_mode) body.response_format = { type: 'json_object' };\n\n const apiResponse = await fetch(DEEPSEEK_API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!apiResponse.ok) {\n const errText = await apiResponse.text();\n return {\n content: [{ type: 'text' as const, text: `DeepSeek API Error (${apiResponse.status}): ${errText}` }],\n isError: true,\n };\n }\n\n // Read SSE stream and accumulate chunks\n let content = '';\n let reasoningContent = '';\n let model = '';\n let finishReason = '';\n let usage: Record = {};\n const toolCalls: Record = {};\n\n const reader = apiResponse.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue;\n const data = line.slice(6).trim();\n if (data === '[DONE]') continue;\n\n try {\n const chunk = JSON.parse(data);\n if (chunk.model) model = chunk.model;\n if (chunk.usage) usage = chunk.usage;\n\n const delta = chunk.choices?.[0]?.delta;\n if (!delta) {\n if (chunk.choices?.[0]?.finish_reason) finishReason = chunk.choices[0].finish_reason;\n continue;\n }\n\n if (delta.content) content += delta.content;\n if (delta.reasoning_content) reasoningContent += delta.reasoning_content;\n if (delta.finish_reason) finishReason = delta.finish_reason;\n\n // Accumulate tool calls\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n if (!toolCalls[idx]) {\n toolCalls[idx] = { id: tc.id || '', type: 'function', function: { name: '', arguments: '' } };\n }\n if (tc.id) toolCalls[idx].id = tc.id;\n if (tc.function?.name) toolCalls[idx].function.name += tc.function.name;\n if (tc.function?.arguments) toolCalls[idx].function.arguments += tc.function.arguments;\n }\n }\n } catch {\n // skip malformed chunks\n }\n }\n }\n\n const toolCallsArray = Object.values(toolCalls);\n const cost = calculateCost(usage);\n\n // Format response text\n let text = '';\n if (reasoningContent) {\n text += `\\n${reasoningContent}\\n\\n\\n`;\n }\n text += content;\n\n if (toolCallsArray.length > 0) {\n text += '\\n\\n**Function Calls:**\\n';\n for (const tc of toolCallsArray) {\n text += `\\`${tc.function.name}\\`\\n`;\n text += `- Call ID: ${tc.id}\\n`;\n text += `- Arguments: ${tc.function.arguments}\\n\\n`;\n }\n }\n\n text += `\\n---\\n**Request Info:**\\n`;\n text += `- **Tokens:** ${usage.total_tokens || 0} (${usage.prompt_tokens || 0} prompt + ${usage.completion_tokens || 0} completion)\\n`;\n text += `- **Model:** ${model}\\n`;\n text += `- **Cost:** ${cost.formatted}`;\n\n return {\n content: [{ type: 'text' as const, text }],\n structuredContent: {\n content,\n reasoning_content: reasoningContent || undefined,\n model,\n usage,\n finish_reason: finishReason,\n tool_calls: toolCallsArray.length > 0 ? toolCallsArray : undefined,\n cost_usd: parseFloat(cost.totalCost.toFixed(6)),\n } as unknown as Record,\n };\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${msg}` }],\n isError: true,\n };\n }\n }\n );\n\n return server;\n}\n\nexport default {\n async fetch(request: Request, _env: Env): Promise {\n const url = new URL(request.url);\n\n // CORS preflight\n if (request.method === 'OPTIONS') {\n return new Response(null, {\n headers: {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization, Mcp-Session-Id',\n },\n });\n }\n\n // MCP Server Card — allows Smithery and other registries to discover capabilities without scanning\n if (url.pathname === '/.well-known/mcp/server-card.json') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek AI — chat, reasoning, function calling, JSON mode, cost tracking',\n transport: { type: 'streamable-http', url: 'https://deepseek-mcp.tahirl.com/mcp' },\n capabilities: { tools: true, prompts: false, resources: false },\n tools: [\n {\n name: 'deepseek_chat',\n description: 'Chat with DeepSeek AI models (Chat + Reasoner). Supports function calling, thinking mode, JSON output, cost tracking.',\n annotations: {\n title: 'DeepSeek Chat',\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n inputSchema: {\n type: 'object',\n required: ['messages'],\n properties: {\n messages: { type: 'array', description: 'Conversation messages', items: { type: 'object' } },\n model: { type: 'string', enum: ['deepseek-chat', 'deepseek-reasoner'], default: 'deepseek-chat' },\n temperature: { type: 'number', description: 'Sampling temperature (0-2)' },\n max_tokens: { type: 'number', description: 'Max tokens to generate' },\n stream: { type: 'boolean', default: false },\n tools: { type: 'array', description: 'Tool definitions for function calling' },\n tool_choice: { description: 'Tool choice control' },\n thinking: { type: 'object', description: 'Enable thinking mode' },\n json_mode: { type: 'boolean', description: 'Enable JSON output mode' },\n },\n },\n },\n ],\n auth: { type: 'bearer', description: 'DeepSeek API key as Bearer token' },\n icon: 'https://raw.githubusercontent.com/arikusi/deepseek-mcp-server/main/icon.png',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n });\n }\n\n // Health check\n if (url.pathname === '/health') {\n return Response.json({\n status: 'ok',\n version: VERSION,\n transport: 'cloudflare-workers',\n timestamp: new Date().toISOString(),\n });\n }\n\n // Root — info page\n if (url.pathname === '/' && request.method === 'GET') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek AI models — BYOK (Bring Your Own Key)',\n endpoints: { mcp: '/mcp', health: '/health' },\n auth: 'Send your DeepSeek API key as Authorization: Bearer ',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n });\n }\n\n // MCP endpoint\n if (url.pathname === '/mcp') {\n // BYOK: require API key\n const apiKey = extractApiKey(request);\n if (!apiKey) {\n return Response.json(\n { error: 'Authorization required. Send your DeepSeek API key as: Authorization: Bearer ' },\n { status: 401 }\n );\n }\n\n // Stateless: create fresh server + transport per request\n const server = createMcpServer(apiKey);\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse: true,\n });\n await server.connect(transport);\n\n const response = await transport.handleRequest(request);\n\n // Add CORS headers\n const headers = new Headers(response.headers);\n headers.set('Access-Control-Allow-Origin', '*');\n headers.set('Access-Control-Expose-Headers', 'Mcp-Session-Id');\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n return Response.json({ error: 'Not Found' }, { status: 404 });\n },\n};\n"}, "7e9b01148b5022bfb2e8f6b311bfdce43e3a93ef": {"t": 1785749511.5916498, "n": 12836, "h": "e0a91d13ea795d0a", "c": "/**\n * DeepSeek MCP Server — Cloudflare Worker\n * Stateless Streamable HTTP transport with BYOK (Bring Your Own Key)\n *\n * Users send their own DeepSeek API key as Bearer token.\n * The worker proxies MCP requests to DeepSeek API.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';\nimport { z } from 'zod';\n\nconst VERSION = '1.5.0';\nconst DEEPSEEK_API_URL = 'https://api.deepseek.com/chat/completions';\n\n// Pricing per 1M tokens (DeepSeek V3.2 unified)\nconst PRICING = {\n cacheHit: 0.028,\n cacheMiss: 0.28,\n output: 0.42,\n};\n\ninterface Env {}\n\nfunction extractApiKey(request: Request): string | null {\n const auth = request.headers.get('authorization');\n if (!auth?.startsWith('Bearer ')) return null;\n return auth.slice(7);\n}\n\nfunction calculateCost(usage: Record): { totalCost: number; formatted: string } {\n const cacheHit = usage.prompt_cache_hit_tokens || 0;\n const cacheMiss = usage.prompt_cache_miss_tokens || usage.prompt_tokens || 0;\n const output = usage.completion_tokens || 0;\n\n const totalCost =\n (cacheHit * PRICING.cacheHit) / 1_000_000 +\n (cacheMiss * PRICING.cacheMiss) / 1_000_000 +\n (output * PRICING.output) / 1_000_000;\n\n let formatted = `$${totalCost.toFixed(6)}`;\n if (cacheHit > 0) {\n const ratio = ((cacheHit / (cacheHit + cacheMiss)) * 100).toFixed(0);\n formatted += ` (cache hit: ${ratio}%)`;\n }\n return { totalCost, formatted };\n}\n\nfunction createMcpServer(apiKey: string): McpServer {\n const server = new McpServer({ name: 'deepseek-mcp-server', version: VERSION });\n\n server.tool(\n 'deepseek_chat',\n 'Chat with DeepSeek AI models (Chat + Reasoner). Supports function calling, thinking mode, JSON output, cost tracking.',\n {\n messages: z\n .array(\n z.object({\n role: z.enum(['system', 'user', 'assistant', 'tool']),\n content: z.string(),\n tool_call_id: z.string().optional(),\n })\n )\n .min(1)\n .describe('Conversation messages'),\n model: z\n .enum(['deepseek-chat', 'deepseek-reasoner'])\n .default('deepseek-chat')\n .describe('Model to use'),\n temperature: z.number().min(0).max(2).optional().describe('Sampling temperature (0-2)'),\n max_tokens: z.number().min(1).max(65536).optional().describe('Max tokens to generate'),\n stream: z.boolean().default(false).describe('Enable streaming (accumulated response returned)'),\n tools: z\n .array(\n z.object({\n type: z.literal('function'),\n function: z.object({\n name: z.string().min(1),\n description: z.string().optional(),\n parameters: z.record(z.string(), z.unknown()).optional(),\n }),\n })\n )\n .max(128)\n .optional()\n .describe('Tool definitions for function calling'),\n tool_choice: z\n .union([\n z.enum(['auto', 'none', 'required']),\n z.object({\n type: z.literal('function'),\n function: z.object({ name: z.string().min(1) }),\n }),\n ])\n .optional()\n .describe('Tool choice control'),\n thinking: z\n .object({ type: z.enum(['enabled', 'disabled']) })\n .optional()\n .describe('Enable thinking mode'),\n json_mode: z.boolean().optional().describe('Enable JSON output mode'),\n },\n async (params) => {\n try {\n // Build DeepSeek API request body — always stream to avoid CF timeout\n const body: Record = {\n model: params.model,\n messages: params.messages,\n stream: true,\n };\n if (params.temperature !== undefined) body.temperature = params.temperature;\n if (params.max_tokens !== undefined) body.max_tokens = params.max_tokens;\n if (params.tools) body.tools = params.tools;\n if (params.tool_choice) body.tool_choice = params.tool_choice;\n if (params.thinking) body.thinking = params.thinking;\n if (params.json_mode) body.response_format = { type: 'json_object' };\n\n const apiResponse = await fetch(DEEPSEEK_API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!apiResponse.ok) {\n const errText = await apiResponse.text();\n return {\n content: [{ type: 'text' as const, text: `DeepSeek API Error (${apiResponse.status}): ${errText}` }],\n isError: true,\n };\n }\n\n // Read SSE stream and accumulate chunks\n let content = '';\n let reasoningContent = '';\n let model = '';\n let finishReason = '';\n let usage: Record = {};\n const toolCalls: Record = {};\n\n const reader = apiResponse.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue;\n const data = line.slice(6).trim();\n if (data === '[DONE]') continue;\n\n try {\n const chunk = JSON.parse(data);\n if (chunk.model) model = chunk.model;\n if (chunk.usage) usage = chunk.usage;\n\n const delta = chunk.choices?.[0]?.delta;\n if (!delta) {\n if (chunk.choices?.[0]?.finish_reason) finishReason = chunk.choices[0].finish_reason;\n continue;\n }\n\n if (delta.content) content += delta.content;\n if (delta.reasoning_content) reasoningContent += delta.reasoning_content;\n if (delta.finish_reason) finishReason = delta.finish_reason;\n\n // Accumulate tool calls\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n if (!toolCalls[idx]) {\n toolCalls[idx] = { id: tc.id || '', type: 'function', function: { name: '', arguments: '' } };\n }\n if (tc.id) toolCalls[idx].id = tc.id;\n if (tc.function?.name) toolCalls[idx].function.name += tc.function.name;\n if (tc.function?.arguments) toolCalls[idx].function.arguments += tc.function.arguments;\n }\n }\n } catch {\n // skip malformed chunks\n }\n }\n }\n\n const toolCallsArray = Object.values(toolCalls);\n const cost = calculateCost(usage);\n\n // Format response text\n let text = '';\n if (reasoningContent) {\n text += `\\n${reasoningContent}\\n\\n\\n`;\n }\n text += content;\n\n if (toolCallsArray.length > 0) {\n text += '\\n\\n**Function Calls:**\\n';\n for (const tc of toolCallsArray) {\n text += `\\`${tc.function.name}\\`\\n`;\n text += `- Call ID: ${tc.id}\\n`;\n text += `- Arguments: ${tc.function.arguments}\\n\\n`;\n }\n }\n\n text += `\\n---\\n**Request Info:**\\n`;\n text += `- **Tokens:** ${usage.total_tokens || 0} (${usage.prompt_tokens || 0} prompt + ${usage.completion_tokens || 0} completion)\\n`;\n text += `- **Model:** ${model}\\n`;\n text += `- **Cost:** ${cost.formatted}`;\n\n return {\n content: [{ type: 'text' as const, text }],\n structuredContent: {\n content,\n reasoning_content: reasoningContent || undefined,\n model,\n usage,\n finish_reason: finishReason,\n tool_calls: toolCallsArray.length > 0 ? toolCallsArray : undefined,\n cost_usd: parseFloat(cost.totalCost.toFixed(6)),\n } as unknown as Record,\n };\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${msg}` }],\n isError: true,\n };\n }\n }\n );\n\n return server;\n}\n\nexport default {\n async fetch(request: Request, _env: Env): Promise {\n const url = new URL(request.url);\n\n // CORS preflight\n if (request.method === 'OPTIONS') {\n return new Response(null, {\n headers: {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization, Mcp-Session-Id',\n },\n });\n }\n\n // MCP Server Card — allows Smithery and other registries to discover capabilities without scanning\n if (url.pathname === '/.well-known/mcp/server-card.json') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek AI — chat, reasoning, function calling, JSON mode, cost tracking',\n transport: { type: 'streamable-http', url: 'https://deepseek-mcp.tahirl.com/mcp' },\n capabilities: { tools: true, prompts: false, resources: false },\n tools: [\n {\n name: 'deepseek_chat',\n description: 'Chat with DeepSeek AI models (Chat + Reasoner). Supports function calling, thinking mode, JSON output, cost tracking.',\n inputSchema: {\n type: 'object',\n required: ['messages'],\n properties: {\n messages: { type: 'array', description: 'Conversation messages', items: { type: 'object' } },\n model: { type: 'string', enum: ['deepseek-chat', 'deepseek-reasoner'], default: 'deepseek-chat' },\n temperature: { type: 'number', description: 'Sampling temperature (0-2)' },\n max_tokens: { type: 'number', description: 'Max tokens to generate' },\n stream: { type: 'boolean', default: false },\n tools: { type: 'array', description: 'Tool definitions for function calling' },\n tool_choice: { description: 'Tool choice control' },\n thinking: { type: 'object', description: 'Enable thinking mode' },\n json_mode: { type: 'boolean', description: 'Enable JSON output mode' },\n },\n },\n },\n ],\n auth: { type: 'bearer', description: 'DeepSeek API key as Bearer token' },\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n });\n }\n\n // Health check\n if (url.pathname === '/health') {\n return Response.json({\n status: 'ok',\n version: VERSION,\n transport: 'cloudflare-workers',\n timestamp: new Date().toISOString(),\n });\n }\n\n // Root — info page\n if (url.pathname === '/' && request.method === 'GET') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek AI models — BYOK (Bring Your Own Key)',\n endpoints: { mcp: '/mcp', health: '/health' },\n auth: 'Send your DeepSeek API key as Authorization: Bearer ',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n });\n }\n\n // MCP endpoint\n if (url.pathname === '/mcp') {\n // BYOK: require API key\n const apiKey = extractApiKey(request);\n if (!apiKey) {\n return Response.json(\n { error: 'Authorization required. Send your DeepSeek API key as: Authorization: Bearer ' },\n { status: 401 }\n );\n }\n\n // Stateless: create fresh server + transport per request\n const server = createMcpServer(apiKey);\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse: true,\n });\n await server.connect(transport);\n\n const response = await transport.handleRequest(request);\n\n // Add CORS headers\n const headers = new Headers(response.headers);\n headers.set('Access-Control-Allow-Origin', '*');\n headers.set('Access-Control-Expose-Headers', 'Mcp-Session-Id');\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n return Response.json({ error: 'Not Found' }, { status: 404 });\n },\n};\n"}, "fc7f7aec18e535a6ef39034346381713e56bc369": {"t": 1785749514.7531748, "n": 10944, "h": "58c6af6184cec55f", "c": "/**\n * DeepSeek MCP Server — Cloudflare Worker\n * Stateless Streamable HTTP transport with BYOK (Bring Your Own Key)\n *\n * Users send their own DeepSeek API key as Bearer token.\n * The worker proxies MCP requests to DeepSeek API.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';\nimport { z } from 'zod';\n\nconst VERSION = '1.5.0';\nconst DEEPSEEK_API_URL = 'https://api.deepseek.com/chat/completions';\n\n// Pricing per 1M tokens (DeepSeek V3.2 unified)\nconst PRICING = {\n cacheHit: 0.028,\n cacheMiss: 0.28,\n output: 0.42,\n};\n\ninterface Env {}\n\nfunction extractApiKey(request: Request): string | null {\n const auth = request.headers.get('authorization');\n if (!auth?.startsWith('Bearer ')) return null;\n return auth.slice(7);\n}\n\nfunction calculateCost(usage: Record): { totalCost: number; formatted: string } {\n const cacheHit = usage.prompt_cache_hit_tokens || 0;\n const cacheMiss = usage.prompt_cache_miss_tokens || usage.prompt_tokens || 0;\n const output = usage.completion_tokens || 0;\n\n const totalCost =\n (cacheHit * PRICING.cacheHit) / 1_000_000 +\n (cacheMiss * PRICING.cacheMiss) / 1_000_000 +\n (output * PRICING.output) / 1_000_000;\n\n let formatted = `$${totalCost.toFixed(6)}`;\n if (cacheHit > 0) {\n const ratio = ((cacheHit / (cacheHit + cacheMiss)) * 100).toFixed(0);\n formatted += ` (cache hit: ${ratio}%)`;\n }\n return { totalCost, formatted };\n}\n\nfunction createMcpServer(apiKey: string): McpServer {\n const server = new McpServer({ name: 'deepseek-mcp-server', version: VERSION });\n\n server.tool(\n 'deepseek_chat',\n 'Chat with DeepSeek AI models (Chat + Reasoner). Supports function calling, thinking mode, JSON output, cost tracking.',\n {\n messages: z\n .array(\n z.object({\n role: z.enum(['system', 'user', 'assistant', 'tool']),\n content: z.string(),\n tool_call_id: z.string().optional(),\n })\n )\n .min(1)\n .describe('Conversation messages'),\n model: z\n .enum(['deepseek-chat', 'deepseek-reasoner'])\n .default('deepseek-chat')\n .describe('Model to use'),\n temperature: z.number().min(0).max(2).optional().describe('Sampling temperature (0-2)'),\n max_tokens: z.number().min(1).max(65536).optional().describe('Max tokens to generate'),\n stream: z.boolean().default(false).describe('Enable streaming (accumulated response returned)'),\n tools: z\n .array(\n z.object({\n type: z.literal('function'),\n function: z.object({\n name: z.string().min(1),\n description: z.string().optional(),\n parameters: z.record(z.string(), z.unknown()).optional(),\n }),\n })\n )\n .max(128)\n .optional()\n .describe('Tool definitions for function calling'),\n tool_choice: z\n .union([\n z.enum(['auto', 'none', 'required']),\n z.object({\n type: z.literal('function'),\n function: z.object({ name: z.string().min(1) }),\n }),\n ])\n .optional()\n .describe('Tool choice control'),\n thinking: z\n .object({ type: z.enum(['enabled', 'disabled']) })\n .optional()\n .describe('Enable thinking mode'),\n json_mode: z.boolean().optional().describe('Enable JSON output mode'),\n },\n async (params) => {\n try {\n // Build DeepSeek API request body — always stream to avoid CF timeout\n const body: Record = {\n model: params.model,\n messages: params.messages,\n stream: true,\n };\n if (params.temperature !== undefined) body.temperature = params.temperature;\n if (params.max_tokens !== undefined) body.max_tokens = params.max_tokens;\n if (params.tools) body.tools = params.tools;\n if (params.tool_choice) body.tool_choice = params.tool_choice;\n if (params.thinking) body.thinking = params.thinking;\n if (params.json_mode) body.response_format = { type: 'json_object' };\n\n const apiResponse = await fetch(DEEPSEEK_API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!apiResponse.ok) {\n const errText = await apiResponse.text();\n return {\n content: [{ type: 'text' as const, text: `DeepSeek API Error (${apiResponse.status}): ${errText}` }],\n isError: true,\n };\n }\n\n // Read SSE stream and accumulate chunks\n let content = '';\n let reasoningContent = '';\n let model = '';\n let finishReason = '';\n let usage: Record = {};\n const toolCalls: Record = {};\n\n const reader = apiResponse.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue;\n const data = line.slice(6).trim();\n if (data === '[DONE]') continue;\n\n try {\n const chunk = JSON.parse(data);\n if (chunk.model) model = chunk.model;\n if (chunk.usage) usage = chunk.usage;\n\n const delta = chunk.choices?.[0]?.delta;\n if (!delta) {\n if (chunk.choices?.[0]?.finish_reason) finishReason = chunk.choices[0].finish_reason;\n continue;\n }\n\n if (delta.content) content += delta.content;\n if (delta.reasoning_content) reasoningContent += delta.reasoning_content;\n if (delta.finish_reason) finishReason = delta.finish_reason;\n\n // Accumulate tool calls\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n if (!toolCalls[idx]) {\n toolCalls[idx] = { id: tc.id || '', type: 'function', function: { name: '', arguments: '' } };\n }\n if (tc.id) toolCalls[idx].id = tc.id;\n if (tc.function?.name) toolCalls[idx].function.name += tc.function.name;\n if (tc.function?.arguments) toolCalls[idx].function.arguments += tc.function.arguments;\n }\n }\n } catch {\n // skip malformed chunks\n }\n }\n }\n\n const toolCallsArray = Object.values(toolCalls);\n const cost = calculateCost(usage);\n\n // Format response text\n let text = '';\n if (reasoningContent) {\n text += `\\n${reasoningContent}\\n\\n\\n`;\n }\n text += content;\n\n if (toolCallsArray.length > 0) {\n text += '\\n\\n**Function Calls:**\\n';\n for (const tc of toolCallsArray) {\n text += `\\`${tc.function.name}\\`\\n`;\n text += `- Call ID: ${tc.id}\\n`;\n text += `- Arguments: ${tc.function.arguments}\\n\\n`;\n }\n }\n\n text += `\\n---\\n**Request Info:**\\n`;\n text += `- **Tokens:** ${usage.total_tokens || 0} (${usage.prompt_tokens || 0} prompt + ${usage.completion_tokens || 0} completion)\\n`;\n text += `- **Model:** ${model}\\n`;\n text += `- **Cost:** ${cost.formatted}`;\n\n return {\n content: [{ type: 'text' as const, text }],\n structuredContent: {\n content,\n reasoning_content: reasoningContent || undefined,\n model,\n usage,\n finish_reason: finishReason,\n tool_calls: toolCallsArray.length > 0 ? toolCallsArray : undefined,\n cost_usd: parseFloat(cost.totalCost.toFixed(6)),\n } as unknown as Record,\n };\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${msg}` }],\n isError: true,\n };\n }\n }\n );\n\n return server;\n}\n\nexport default {\n async fetch(request: Request, _env: Env): Promise {\n const url = new URL(request.url);\n\n // CORS preflight\n if (request.method === 'OPTIONS') {\n return new Response(null, {\n headers: {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization, Mcp-Session-Id',\n },\n });\n }\n\n // Health check\n if (url.pathname === '/health') {\n return Response.json({\n status: 'ok',\n version: VERSION,\n transport: 'cloudflare-workers',\n timestamp: new Date().toISOString(),\n });\n }\n\n // Root — info page\n if (url.pathname === '/' && request.method === 'GET') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek AI models — BYOK (Bring Your Own Key)',\n endpoints: { mcp: '/mcp', health: '/health' },\n auth: 'Send your DeepSeek API key as Authorization: Bearer ',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n });\n }\n\n // MCP endpoint\n if (url.pathname === '/mcp') {\n // BYOK: require API key\n const apiKey = extractApiKey(request);\n if (!apiKey) {\n return Response.json(\n { error: 'Authorization required. Send your DeepSeek API key as: Authorization: Bearer ' },\n { status: 401 }\n );\n }\n\n // Stateless: create fresh server + transport per request\n const server = createMcpServer(apiKey);\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse: true,\n });\n await server.connect(transport);\n\n const response = await transport.handleRequest(request);\n\n // Add CORS headers\n const headers = new Headers(response.headers);\n headers.set('Access-Control-Allow-Origin', '*');\n headers.set('Access-Control-Expose-Headers', 'Mcp-Session-Id');\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n return Response.json({ error: 'Not Found' }, { status: 404 });\n },\n};\n"}, "0bbf34b020d78778373f6ba619fbc131d9091c2e": {"t": 1785749515.8667634, "n": 11425, "h": "d71c660a136ea663", "c": "/**\n * DeepSeek MCP Server — Cloudflare Worker\n * Stateless Streamable HTTP transport with BYOK (Bring Your Own Key)\n *\n * Users send their own DeepSeek API key as Bearer token.\n * The worker proxies MCP requests to DeepSeek API.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';\nimport { z } from 'zod';\n\nconst VERSION = '1.5.0';\nconst DEEPSEEK_API_URL = 'https://api.deepseek.com/chat/completions';\n\n// Pricing per 1M tokens (DeepSeek V3.2 unified)\nconst PRICING = {\n cacheHit: 0.028,\n cacheMiss: 0.28,\n output: 0.42,\n};\n\ninterface Env {}\n\nfunction extractApiKey(request: Request): string | null {\n const auth = request.headers.get('authorization');\n if (!auth?.startsWith('Bearer ')) return null;\n return auth.slice(7);\n}\n\nfunction calculateCost(usage: Record): { totalCost: number; formatted: string } {\n const cacheHit = usage.prompt_cache_hit_tokens || 0;\n const cacheMiss = usage.prompt_cache_miss_tokens || usage.prompt_tokens || 0;\n const output = usage.completion_tokens || 0;\n\n const totalCost =\n (cacheHit * PRICING.cacheHit) / 1_000_000 +\n (cacheMiss * PRICING.cacheMiss) / 1_000_000 +\n (output * PRICING.output) / 1_000_000;\n\n let formatted = `$${totalCost.toFixed(6)}`;\n if (cacheHit > 0) {\n const ratio = ((cacheHit / (cacheHit + cacheMiss)) * 100).toFixed(0);\n formatted += ` (cache hit: ${ratio}%)`;\n }\n return { totalCost, formatted };\n}\n\nfunction createMcpServer(apiKey: string): McpServer {\n const server = new McpServer({ name: 'deepseek-mcp-server', version: VERSION });\n\n server.tool(\n 'deepseek_chat',\n 'Chat with DeepSeek AI models (Chat + Reasoner). Supports function calling, thinking mode, JSON output, cost tracking.',\n {\n messages: z\n .array(\n z.object({\n role: z.enum(['system', 'user', 'assistant', 'tool']),\n content: z.string(),\n tool_call_id: z.string().optional(),\n })\n )\n .min(1)\n .describe('Conversation messages'),\n model: z\n .enum(['deepseek-chat', 'deepseek-reasoner'])\n .default('deepseek-chat')\n .describe('Model to use'),\n temperature: z.number().min(0).max(2).optional().describe('Sampling temperature (0-2)'),\n max_tokens: z.number().min(1).max(65536).optional().describe('Max tokens to generate'),\n stream: z.boolean().default(false).describe('Enable streaming (accumulated response returned)'),\n tools: z\n .array(\n z.object({\n type: z.literal('function'),\n function: z.object({\n name: z.string().min(1),\n description: z.string().optional(),\n parameters: z.record(z.string(), z.unknown()).optional(),\n }),\n })\n )\n .max(128)\n .optional()\n .describe('Tool definitions for function calling'),\n tool_choice: z\n .union([\n z.enum(['auto', 'none', 'required']),\n z.object({\n type: z.literal('function'),\n function: z.object({ name: z.string().min(1) }),\n }),\n ])\n .optional()\n .describe('Tool choice control'),\n thinking: z\n .object({ type: z.enum(['enabled', 'disabled']) })\n .optional()\n .describe('Enable thinking mode'),\n json_mode: z.boolean().optional().describe('Enable JSON output mode'),\n },\n async (params) => {\n try {\n // Build DeepSeek API request body — always stream to avoid CF timeout\n const body: Record = {\n model: params.model,\n messages: params.messages,\n stream: true,\n };\n if (params.temperature !== undefined) body.temperature = params.temperature;\n if (params.max_tokens !== undefined) body.max_tokens = params.max_tokens;\n if (params.tools) body.tools = params.tools;\n if (params.tool_choice) body.tool_choice = params.tool_choice;\n if (params.thinking) body.thinking = params.thinking;\n if (params.json_mode) body.response_format = { type: 'json_object' };\n\n const apiResponse = await fetch(DEEPSEEK_API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!apiResponse.ok) {\n const errText = await apiResponse.text();\n return {\n content: [{ type: 'text' as const, text: `DeepSeek API Error (${apiResponse.status}): ${errText}` }],\n isError: true,\n };\n }\n\n // Read SSE stream and accumulate chunks\n let content = '';\n let reasoningContent = '';\n let model = '';\n let finishReason = '';\n let usage: Record = {};\n const toolCalls: Record = {};\n\n const reader = apiResponse.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue;\n const data = line.slice(6).trim();\n if (data === '[DONE]') continue;\n\n try {\n const chunk = JSON.parse(data);\n if (chunk.model) model = chunk.model;\n if (chunk.usage) usage = chunk.usage;\n\n const delta = chunk.choices?.[0]?.delta;\n if (!delta) {\n if (chunk.choices?.[0]?.finish_reason) finishReason = chunk.choices[0].finish_reason;\n continue;\n }\n\n if (delta.content) content += delta.content;\n if (delta.reasoning_content) reasoningContent += delta.reasoning_content;\n if (delta.finish_reason) finishReason = delta.finish_reason;\n\n // Accumulate tool calls\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n if (!toolCalls[idx]) {\n toolCalls[idx] = { id: tc.id || '', type: 'function', function: { name: '', arguments: '' } };\n }\n if (tc.id) toolCalls[idx].id = tc.id;\n if (tc.function?.name) toolCalls[idx].function.name += tc.function.name;\n if (tc.function?.arguments) toolCalls[idx].function.arguments += tc.function.arguments;\n }\n }\n } catch {\n // skip malformed chunks\n }\n }\n }\n\n const toolCallsArray = Object.values(toolCalls);\n const cost = calculateCost(usage);\n\n // Format response text\n let text = '';\n if (reasoningContent) {\n text += `\\n${reasoningContent}\\n\\n\\n`;\n }\n text += content;\n\n if (toolCallsArray.length > 0) {\n text += '\\n\\n**Function Calls:**\\n';\n for (const tc of toolCallsArray) {\n text += `\\`${tc.function.name}\\`\\n`;\n text += `- Call ID: ${tc.id}\\n`;\n text += `- Arguments: ${tc.function.arguments}\\n\\n`;\n }\n }\n\n text += `\\n---\\n**Request Info:**\\n`;\n text += `- **Tokens:** ${usage.total_tokens || 0} (${usage.prompt_tokens || 0} prompt + ${usage.completion_tokens || 0} completion)\\n`;\n text += `- **Model:** ${model}\\n`;\n text += `- **Cost:** ${cost.formatted}`;\n\n return {\n content: [{ type: 'text' as const, text }],\n structuredContent: {\n content,\n reasoning_content: reasoningContent || undefined,\n model,\n usage,\n finish_reason: finishReason,\n tool_calls: toolCallsArray.length > 0 ? toolCallsArray : undefined,\n cost_usd: parseFloat(cost.totalCost.toFixed(6)),\n } as unknown as Record,\n };\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${msg}` }],\n isError: true,\n };\n }\n }\n );\n\n return server;\n}\n\nexport default {\n async fetch(request: Request, _env: Env): Promise {\n const url = new URL(request.url);\n\n // CORS preflight\n if (request.method === 'OPTIONS') {\n return new Response(null, {\n headers: {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization, Mcp-Session-Id',\n },\n });\n }\n\n // Health check\n if (url.pathname === '/health') {\n return Response.json({\n status: 'ok',\n version: VERSION,\n transport: 'cloudflare-workers',\n timestamp: new Date().toISOString(),\n });\n }\n\n // Root — info page\n if (url.pathname === '/' && request.method === 'GET') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek AI models — BYOK (Bring Your Own Key)',\n endpoints: { mcp: '/mcp', health: '/health' },\n auth: 'Send your DeepSeek API key as Authorization: Bearer ',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n });\n }\n\n // MCP endpoint\n if (url.pathname === '/mcp') {\n const apiKey = extractApiKey(request);\n\n // Allow discovery requests (initialize, tools/list, etc.) without auth\n // Only require API key for actual tool calls (tools/call)\n let isToolCall = false;\n if (request.method === 'POST') {\n try {\n const cloned = request.clone();\n const body = await cloned.json() as Record;\n isToolCall = body.method === 'tools/call';\n } catch {\n // not JSON — let MCP SDK handle the error\n }\n }\n\n if (isToolCall && !apiKey) {\n return Response.json(\n { error: 'Authorization required. Send your DeepSeek API key as: Authorization: Bearer ' },\n { status: 401 }\n );\n }\n\n // Stateless: create fresh server + transport per request\n const server = createMcpServer(apiKey || 'anonymous');\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse: true,\n });\n await server.connect(transport);\n\n const response = await transport.handleRequest(request);\n\n // Add CORS headers\n const headers = new Headers(response.headers);\n headers.set('Access-Control-Allow-Origin', '*');\n headers.set('Access-Control-Expose-Headers', 'Mcp-Session-Id');\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n return Response.json({ error: 'Not Found' }, { status: 404 });\n },\n};\n"}, "6404cc1e74c52c9575484959a0bb1790636732f3": {"t": 1785749517.495095, "n": 10944, "h": "58c6af6184cec55f", "c": "/**\n * DeepSeek MCP Server — Cloudflare Worker\n * Stateless Streamable HTTP transport with BYOK (Bring Your Own Key)\n *\n * Users send their own DeepSeek API key as Bearer token.\n * The worker proxies MCP requests to DeepSeek API.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';\nimport { z } from 'zod';\n\nconst VERSION = '1.5.0';\nconst DEEPSEEK_API_URL = 'https://api.deepseek.com/chat/completions';\n\n// Pricing per 1M tokens (DeepSeek V3.2 unified)\nconst PRICING = {\n cacheHit: 0.028,\n cacheMiss: 0.28,\n output: 0.42,\n};\n\ninterface Env {}\n\nfunction extractApiKey(request: Request): string | null {\n const auth = request.headers.get('authorization');\n if (!auth?.startsWith('Bearer ')) return null;\n return auth.slice(7);\n}\n\nfunction calculateCost(usage: Record): { totalCost: number; formatted: string } {\n const cacheHit = usage.prompt_cache_hit_tokens || 0;\n const cacheMiss = usage.prompt_cache_miss_tokens || usage.prompt_tokens || 0;\n const output = usage.completion_tokens || 0;\n\n const totalCost =\n (cacheHit * PRICING.cacheHit) / 1_000_000 +\n (cacheMiss * PRICING.cacheMiss) / 1_000_000 +\n (output * PRICING.output) / 1_000_000;\n\n let formatted = `$${totalCost.toFixed(6)}`;\n if (cacheHit > 0) {\n const ratio = ((cacheHit / (cacheHit + cacheMiss)) * 100).toFixed(0);\n formatted += ` (cache hit: ${ratio}%)`;\n }\n return { totalCost, formatted };\n}\n\nfunction createMcpServer(apiKey: string): McpServer {\n const server = new McpServer({ name: 'deepseek-mcp-server', version: VERSION });\n\n server.tool(\n 'deepseek_chat',\n 'Chat with DeepSeek AI models (Chat + Reasoner). Supports function calling, thinking mode, JSON output, cost tracking.',\n {\n messages: z\n .array(\n z.object({\n role: z.enum(['system', 'user', 'assistant', 'tool']),\n content: z.string(),\n tool_call_id: z.string().optional(),\n })\n )\n .min(1)\n .describe('Conversation messages'),\n model: z\n .enum(['deepseek-chat', 'deepseek-reasoner'])\n .default('deepseek-chat')\n .describe('Model to use'),\n temperature: z.number().min(0).max(2).optional().describe('Sampling temperature (0-2)'),\n max_tokens: z.number().min(1).max(65536).optional().describe('Max tokens to generate'),\n stream: z.boolean().default(false).describe('Enable streaming (accumulated response returned)'),\n tools: z\n .array(\n z.object({\n type: z.literal('function'),\n function: z.object({\n name: z.string().min(1),\n description: z.string().optional(),\n parameters: z.record(z.string(), z.unknown()).optional(),\n }),\n })\n )\n .max(128)\n .optional()\n .describe('Tool definitions for function calling'),\n tool_choice: z\n .union([\n z.enum(['auto', 'none', 'required']),\n z.object({\n type: z.literal('function'),\n function: z.object({ name: z.string().min(1) }),\n }),\n ])\n .optional()\n .describe('Tool choice control'),\n thinking: z\n .object({ type: z.enum(['enabled', 'disabled']) })\n .optional()\n .describe('Enable thinking mode'),\n json_mode: z.boolean().optional().describe('Enable JSON output mode'),\n },\n async (params) => {\n try {\n // Build DeepSeek API request body — always stream to avoid CF timeout\n const body: Record = {\n model: params.model,\n messages: params.messages,\n stream: true,\n };\n if (params.temperature !== undefined) body.temperature = params.temperature;\n if (params.max_tokens !== undefined) body.max_tokens = params.max_tokens;\n if (params.tools) body.tools = params.tools;\n if (params.tool_choice) body.tool_choice = params.tool_choice;\n if (params.thinking) body.thinking = params.thinking;\n if (params.json_mode) body.response_format = { type: 'json_object' };\n\n const apiResponse = await fetch(DEEPSEEK_API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!apiResponse.ok) {\n const errText = await apiResponse.text();\n return {\n content: [{ type: 'text' as const, text: `DeepSeek API Error (${apiResponse.status}): ${errText}` }],\n isError: true,\n };\n }\n\n // Read SSE stream and accumulate chunks\n let content = '';\n let reasoningContent = '';\n let model = '';\n let finishReason = '';\n let usage: Record = {};\n const toolCalls: Record = {};\n\n const reader = apiResponse.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue;\n const data = line.slice(6).trim();\n if (data === '[DONE]') continue;\n\n try {\n const chunk = JSON.parse(data);\n if (chunk.model) model = chunk.model;\n if (chunk.usage) usage = chunk.usage;\n\n const delta = chunk.choices?.[0]?.delta;\n if (!delta) {\n if (chunk.choices?.[0]?.finish_reason) finishReason = chunk.choices[0].finish_reason;\n continue;\n }\n\n if (delta.content) content += delta.content;\n if (delta.reasoning_content) reasoningContent += delta.reasoning_content;\n if (delta.finish_reason) finishReason = delta.finish_reason;\n\n // Accumulate tool calls\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n if (!toolCalls[idx]) {\n toolCalls[idx] = { id: tc.id || '', type: 'function', function: { name: '', arguments: '' } };\n }\n if (tc.id) toolCalls[idx].id = tc.id;\n if (tc.function?.name) toolCalls[idx].function.name += tc.function.name;\n if (tc.function?.arguments) toolCalls[idx].function.arguments += tc.function.arguments;\n }\n }\n } catch {\n // skip malformed chunks\n }\n }\n }\n\n const toolCallsArray = Object.values(toolCalls);\n const cost = calculateCost(usage);\n\n // Format response text\n let text = '';\n if (reasoningContent) {\n text += `\\n${reasoningContent}\\n\\n\\n`;\n }\n text += content;\n\n if (toolCallsArray.length > 0) {\n text += '\\n\\n**Function Calls:**\\n';\n for (const tc of toolCallsArray) {\n text += `\\`${tc.function.name}\\`\\n`;\n text += `- Call ID: ${tc.id}\\n`;\n text += `- Arguments: ${tc.function.arguments}\\n\\n`;\n }\n }\n\n text += `\\n---\\n**Request Info:**\\n`;\n text += `- **Tokens:** ${usage.total_tokens || 0} (${usage.prompt_tokens || 0} prompt + ${usage.completion_tokens || 0} completion)\\n`;\n text += `- **Model:** ${model}\\n`;\n text += `- **Cost:** ${cost.formatted}`;\n\n return {\n content: [{ type: 'text' as const, text }],\n structuredContent: {\n content,\n reasoning_content: reasoningContent || undefined,\n model,\n usage,\n finish_reason: finishReason,\n tool_calls: toolCallsArray.length > 0 ? toolCallsArray : undefined,\n cost_usd: parseFloat(cost.totalCost.toFixed(6)),\n } as unknown as Record,\n };\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${msg}` }],\n isError: true,\n };\n }\n }\n );\n\n return server;\n}\n\nexport default {\n async fetch(request: Request, _env: Env): Promise {\n const url = new URL(request.url);\n\n // CORS preflight\n if (request.method === 'OPTIONS') {\n return new Response(null, {\n headers: {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization, Mcp-Session-Id',\n },\n });\n }\n\n // Health check\n if (url.pathname === '/health') {\n return Response.json({\n status: 'ok',\n version: VERSION,\n transport: 'cloudflare-workers',\n timestamp: new Date().toISOString(),\n });\n }\n\n // Root — info page\n if (url.pathname === '/' && request.method === 'GET') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek AI models — BYOK (Bring Your Own Key)',\n endpoints: { mcp: '/mcp', health: '/health' },\n auth: 'Send your DeepSeek API key as Authorization: Bearer ',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n });\n }\n\n // MCP endpoint\n if (url.pathname === '/mcp') {\n // BYOK: require API key\n const apiKey = extractApiKey(request);\n if (!apiKey) {\n return Response.json(\n { error: 'Authorization required. Send your DeepSeek API key as: Authorization: Bearer ' },\n { status: 401 }\n );\n }\n\n // Stateless: create fresh server + transport per request\n const server = createMcpServer(apiKey);\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse: true,\n });\n await server.connect(transport);\n\n const response = await transport.handleRequest(request);\n\n // Add CORS headers\n const headers = new Headers(response.headers);\n headers.set('Access-Control-Allow-Origin', '*');\n headers.set('Access-Control-Expose-Headers', 'Mcp-Session-Id');\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n return Response.json({ error: 'Not Found' }, { status: 404 });\n },\n};\n"}, "03f0206a0a6601690b33d2028fb8feec4e8300a9": {"t": 1785749518.5832424, "n": 8848, "h": "10c824db482e191a", "c": "/**\n * DeepSeek MCP Server — Cloudflare Worker\n * Stateless Streamable HTTP transport with BYOK (Bring Your Own Key)\n *\n * Users send their own DeepSeek API key as Bearer token.\n * The worker proxies MCP requests to DeepSeek API.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';\nimport { z } from 'zod';\n\nconst VERSION = '1.5.0';\nconst DEEPSEEK_API_URL = 'https://api.deepseek.com/chat/completions';\n\n// Pricing per 1M tokens (DeepSeek V3.2 unified)\nconst PRICING = {\n cacheHit: 0.028,\n cacheMiss: 0.28,\n output: 0.42,\n};\n\ninterface Env {}\n\nfunction extractApiKey(request: Request): string | null {\n const auth = request.headers.get('authorization');\n if (!auth?.startsWith('Bearer ')) return null;\n return auth.slice(7);\n}\n\nfunction calculateCost(usage: Record): { totalCost: number; formatted: string } {\n const cacheHit = usage.prompt_cache_hit_tokens || 0;\n const cacheMiss = usage.prompt_cache_miss_tokens || usage.prompt_tokens || 0;\n const output = usage.completion_tokens || 0;\n\n const totalCost =\n (cacheHit * PRICING.cacheHit) / 1_000_000 +\n (cacheMiss * PRICING.cacheMiss) / 1_000_000 +\n (output * PRICING.output) / 1_000_000;\n\n let formatted = `$${totalCost.toFixed(6)}`;\n if (cacheHit > 0) {\n const ratio = ((cacheHit / (cacheHit + cacheMiss)) * 100).toFixed(0);\n formatted += ` (cache hit: ${ratio}%)`;\n }\n return { totalCost, formatted };\n}\n\nfunction createMcpServer(apiKey: string): McpServer {\n const server = new McpServer({ name: 'deepseek-mcp-server', version: VERSION });\n\n server.tool(\n 'deepseek_chat',\n 'Chat with DeepSeek AI models (Chat + Reasoner). Supports function calling, thinking mode, JSON output, cost tracking.',\n {\n messages: z\n .array(\n z.object({\n role: z.enum(['system', 'user', 'assistant', 'tool']),\n content: z.string(),\n tool_call_id: z.string().optional(),\n })\n )\n .min(1)\n .describe('Conversation messages'),\n model: z\n .enum(['deepseek-chat', 'deepseek-reasoner'])\n .default('deepseek-chat')\n .describe('Model to use'),\n temperature: z.number().min(0).max(2).optional().describe('Sampling temperature (0-2)'),\n max_tokens: z.number().min(1).max(65536).optional().describe('Max tokens to generate'),\n stream: z.boolean().default(false).describe('Enable streaming (accumulated response returned)'),\n tools: z\n .array(\n z.object({\n type: z.literal('function'),\n function: z.object({\n name: z.string().min(1),\n description: z.string().optional(),\n parameters: z.record(z.string(), z.unknown()).optional(),\n }),\n })\n )\n .max(128)\n .optional()\n .describe('Tool definitions for function calling'),\n tool_choice: z\n .union([\n z.enum(['auto', 'none', 'required']),\n z.object({\n type: z.literal('function'),\n function: z.object({ name: z.string().min(1) }),\n }),\n ])\n .optional()\n .describe('Tool choice control'),\n thinking: z\n .object({ type: z.enum(['enabled', 'disabled']) })\n .optional()\n .describe('Enable thinking mode'),\n json_mode: z.boolean().optional().describe('Enable JSON output mode'),\n },\n async (params) => {\n try {\n // Build DeepSeek API request body\n const body: Record = {\n model: params.model,\n messages: params.messages,\n stream: false,\n };\n if (params.temperature !== undefined) body.temperature = params.temperature;\n if (params.max_tokens !== undefined) body.max_tokens = params.max_tokens;\n if (params.tools) body.tools = params.tools;\n if (params.tool_choice) body.tool_choice = params.tool_choice;\n if (params.thinking) body.thinking = params.thinking;\n if (params.json_mode) body.response_format = { type: 'json_object' };\n\n const apiResponse = await fetch(DEEPSEEK_API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!apiResponse.ok) {\n const errText = await apiResponse.text();\n return {\n content: [{ type: 'text' as const, text: `DeepSeek API Error (${apiResponse.status}): ${errText}` }],\n isError: true,\n };\n }\n\n const data = (await apiResponse.json()) as Record;\n const choice = data.choices?.[0];\n const message = choice?.message;\n const usage = data.usage || {};\n const cost = calculateCost(usage);\n\n // Format response text\n let text = '';\n if (message?.reasoning_content) {\n text += `\\n${message.reasoning_content}\\n\\n\\n`;\n }\n text += message?.content || '';\n\n if (message?.tool_calls?.length) {\n text += '\\n\\n**Function Calls:**\\n';\n for (const tc of message.tool_calls) {\n text += `\\`${tc.function.name}\\`\\n`;\n text += `- Call ID: ${tc.id}\\n`;\n text += `- Arguments: ${tc.function.arguments}\\n\\n`;\n }\n }\n\n text += `\\n---\\n**Request Info:**\\n`;\n text += `- **Tokens:** ${usage.total_tokens || 0} (${usage.prompt_tokens || 0} prompt + ${usage.completion_tokens || 0} completion)\\n`;\n text += `- **Model:** ${data.model}\\n`;\n text += `- **Cost:** ${cost.formatted}`;\n\n return {\n content: [{ type: 'text' as const, text }],\n structuredContent: {\n content: message?.content || '',\n reasoning_content: message?.reasoning_content,\n model: data.model,\n usage,\n finish_reason: choice?.finish_reason,\n tool_calls: message?.tool_calls,\n cost_usd: parseFloat(cost.totalCost.toFixed(6)),\n } as unknown as Record,\n };\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${msg}` }],\n isError: true,\n };\n }\n }\n );\n\n return server;\n}\n\nexport default {\n async fetch(request: Request, _env: Env): Promise {\n const url = new URL(request.url);\n\n // CORS preflight\n if (request.method === 'OPTIONS') {\n return new Response(null, {\n headers: {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization, Mcp-Session-Id',\n },\n });\n }\n\n // Health check\n if (url.pathname === '/health') {\n return Response.json({\n status: 'ok',\n version: VERSION,\n transport: 'cloudflare-workers',\n timestamp: new Date().toISOString(),\n });\n }\n\n // Root — info page\n if (url.pathname === '/' && request.method === 'GET') {\n return Response.json({\n name: 'deepseek-mcp-server',\n version: VERSION,\n description: 'MCP server for DeepSeek AI models — BYOK (Bring Your Own Key)',\n endpoints: { mcp: '/mcp', health: '/health' },\n auth: 'Send your DeepSeek API key as Authorization: Bearer ',\n docs: 'https://github.com/arikusi/deepseek-mcp-server',\n });\n }\n\n // MCP endpoint\n if (url.pathname === '/mcp') {\n // BYOK: require API key\n const apiKey = extractApiKey(request);\n if (!apiKey) {\n return Response.json(\n { error: 'Authorization required. Send your DeepSeek API key as: Authorization: Bearer ' },\n { status: 401 }\n );\n }\n\n // Stateless: create fresh server + transport per request\n const server = createMcpServer(apiKey);\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse: true,\n });\n await server.connect(transport);\n\n const response = await transport.handleRequest(request);\n\n // Add CORS headers\n const headers = new Headers(response.headers);\n headers.set('Access-Control-Allow-Origin', '*');\n headers.set('Access-Control-Expose-Headers', 'Mcp-Session-Id');\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n return Response.json({ error: 'Not Found' }, { status: 404 });\n },\n};\n"}}, "llms-full.txt": {"5eb18b60abee16172cb9d04e70b4c46fd470783a": {"t": 1785746526.2633977, "n": 19670, "h": "57c17305efe2c9ca", "c": "# DeepSeek MCP Server - Complete Documentation\n\nA Model Context Protocol (MCP) server that integrates DeepSeek V4 models (deepseek-v4-flash and deepseek-v4-pro, 1M context) with MCP-compatible clients like Claude Code and Gemini CLI. deepseek-chat and deepseek-reasoner are deprecated aliases, still accepted and resolved to v4-flash. Hosted remote endpoint at deepseek-mcp.tahirl.com/mcp with BYOK (Bring Your Own Key) authentication. Also supports local stdio and self-hosted Streamable HTTP transports with Docker deployment. Provides multi-turn sessions, model fallback with circuit breaker, MCP Resources, chat completion, thinking mode, JSON output, function calling, multimodal content support, model-aware cost tracking, and 12 prompt templates.\n\n## Installation\n\n### Remote (No Install)\n\n```bash\n# Claude Code — connect to hosted endpoint with your own API key\nclaude mcp add --transport http deepseek \\\n https://deepseek-mcp.tahirl.com/mcp \\\n --header \"Authorization: Bearer YOUR_DEEPSEEK_API_KEY\"\n```\n\n```json\n// Cursor / Windsurf / VS Code\n{\n \"mcpServers\": {\n \"deepseek\": {\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\",\n \"headers\": { \"Authorization\": \"Bearer ${DEEPSEEK_API_KEY}\" }\n }\n }\n}\n```\n\n### Local (stdio)\n\n```bash\n# Claude Code (all projects)\nclaude mcp add -s user deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Gemini CLI\ngemini mcp add deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Global npm install\nnpm install -g @arikusi/deepseek-mcp-server\n\n# Docker (build locally, bind to loopback with a token)\ndocker build -t deepseek-mcp-server . && docker run -d -p 127.0.0.1:3000:3000 -e DEEPSEEK_API_KEY=your-key -e HTTP_AUTH_TOKEN=your-token deepseek-mcp-server\n```\n\nGet API key: https://platform.deepseek.com\n\n## Models\n\ndeepseek-v4-flash and deepseek-v4-pro are the live V4 models (1M context, up to 384K output). deepseek-chat and deepseek-reasoner are deprecated compatibility aliases: still accepted and resolved to deepseek-v4-flash, but slated for removal in the next major release. The DeepSeek API itself retired those two names on 2026-07-24. All calls are non-thinking by default for fast responses; enable reasoning with thinking:{type:\"enabled\"}.\n\n### deepseek-v4-flash (default)\n- Fast, economical. General conversations, coding, content generation, agent loops\n- 1M context, up to 384K max output\n- Supports thinking mode, JSON mode, function calling\n- Pricing: $0.0028/1M cache hit, $0.14/1M cache miss, $0.28/1M output\n\n### deepseek-v4-pro\n- Most capable. Complex reasoning, math, hard multi-step tasks\n- 1M context, up to 384K max output\n- Supports thinking mode, JSON mode, function calling\n- Pricing: $0.003625/1M cache hit, $0.435/1M cache miss, $0.87/1M output\n\n### Model Routing Table\n\n| User selects | Sent to API | Thinking | reasoning_content |\n|---|---|---|---|\n| deepseek-v4-flash | deepseek-v4-flash | Off (default) | No |\n| deepseek-v4-flash + thinking:enabled | deepseek-v4-flash | On | Yes |\n| deepseek-v4-pro | deepseek-v4-pro | Off (default) | No |\n| deepseek-chat (deprecated alias) | deepseek-v4-flash | Off | No |\n| deepseek-reasoner (deprecated alias) | deepseek-v4-flash | On | Yes |\n\n## Tool: deepseek_chat\n\n### Input Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| messages | Array<{role, content, tool_call_id?}> | Yes | - | Conversation messages. Roles: system, user, assistant, tool. Content can be string or array of content parts (text/image_url) when multimodal is enabled |\n| model | \"deepseek-v4-flash\" \\| \"deepseek-v4-pro\" \\| \"deepseek-chat\" \\| \"deepseek-reasoner\" | No | \"deepseek-v4-flash\" | Model to use (chat/reasoner are deprecated aliases for v4-flash, still accepted) |\n| temperature | number (0-2) | No | 1.0 | Sampling temperature. Ignored when thinking enabled |\n| max_tokens | number (1-384000) | No | model default | Max output tokens (V4 supports up to 384000; 1M context) |\n| stream | boolean | No | false | Enable streaming mode |\n| tools | Array (max 128) | No | - | Function calling tool definitions |\n| tool_choice | \"auto\" \\| \"none\" \\| \"required\" \\| {type, function} | No | \"auto\" | Which tool to call |\n| thinking | {type: \"enabled\" \\| \"disabled\"} | No | disabled | Toggle thinking mode (non-thinking by default) |\n| reasoning_effort | \"high\" \\| \"max\" | No | high | Reasoning effort while thinking is active |\n| json_mode | boolean | No | false | Enable JSON output mode (supported by both models) |\n| response_schema | object (JSON Schema) | No | - | Validate the model output against this JSON Schema. Implies JSON output. On mismatch, up to RESPONSE_SCHEMA_MAX_RETRIES repair retries feed the error back to the model. Schema regex patterns are ReDoS-screened; an unsafe pattern is rejected |\n| session_id | string | No | - | Session ID for multi-turn conversations |\n\n### Multi-Turn Sessions\n\nUse `session_id` to maintain conversation context across requests:\n\n```json\n// First request - creates session\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What is the capital of France?\"}],\n \"session_id\": \"my-session\"\n}\n\n// Second request - previous context is automatically prepended\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What about Germany?\"}],\n \"session_id\": \"my-session\"\n}\n```\n\nSessions are stored in memory. In STDIO transport they live for the lifetime of the server process. In HTTP transport each MCP session gets its own isolated SessionStore, so session_id values are scoped to the MCP session that created them and are not visible to other connected HTTP clients. Sessions expire after SESSION_TTL_MINUTES (default: 30). Omit session_id for stateless single-turn requests.\n\n### Thinking Mode\n\nEnable thinking with the thinking parameter:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Analyze quicksort complexity\"}],\n \"model\": \"deepseek-v4-flash\",\n \"thinking\": {\"type\": \"enabled\"}\n}\n```\n\nThe deprecated deepseek-reasoner alias produces the same result (it is routed as v4-flash + thinking). When thinking is active, temperature/top_p/frequency_penalty/presence_penalty are automatically ignored. The response includes a reasoning_content field with the chain-of-thought.\n\n### JSON Output Mode\n\nGet structured JSON responses:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Return a json object with user data\"}],\n \"model\": \"deepseek-v4-flash\",\n \"json_mode\": true\n}\n```\n\nInclude the word \"json\" in your prompt for best results. Supported by both models. When JSON output is requested, the returned content is deterministically recovered as a clean JSON value even if the model wrapped it in a code fence or leaked chain-of-thought around it (no extra API call); if nothing parseable is found the raw text is preserved and json_parse_error is set.\n\n### Schema-Validated JSON\n\nPass a response_schema (a JSON Schema) to constrain the output shape, not just its syntax:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Classify this sentiment as json: \\\"loved it\\\"\"}],\n \"model\": \"deepseek-v4-flash\",\n \"response_schema\": {\n \"type\": \"object\",\n \"properties\": {\n \"sentiment\": {\"type\": \"string\", \"enum\": [\"positive\", \"negative\", \"neutral\"]},\n \"confidence\": {\"type\": \"number\", \"minimum\": 0, \"maximum\": 1}\n },\n \"required\": [\"sentiment\", \"confidence\"],\n \"additionalProperties\": false\n }\n}\n```\n\nThe server validates the parsed output with Ajv. On a mismatch it issues up to RESPONSE_SCHEMA_MAX_RETRIES repair retries (default 2, set 0 for a single deterministic call), feeding the validation error back to the model, and returns the first schema-valid object. A persistent mismatch surfaces as structuredContent.schema = {valid: false, attempts, error} rather than a silently coerced answer. Regex patterns in the schema are screened for catastrophic backtracking (ReDoS) before compilation; an unsafe pattern such as ^(a+)+$ is rejected up front as an invalid schema so it can never block the event loop.\n\n### Tool Definition Format\n\n```json\n{\n \"type\": \"function\",\n \"function\": {\n \"name\": \"function_name\",\n \"description\": \"What the function does\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"param1\": { \"type\": \"string\", \"description\": \"...\" }\n },\n \"required\": [\"param1\"]\n }\n }\n}\n```\n\n### Response Format\n\n```json\n{\n \"content\": \"Response text\",\n \"reasoning_content\": \"Chain-of-thought (thinking mode only)\",\n \"model\": \"deepseek-v4-flash\",\n \"usage\": {\n \"prompt_tokens\": 10,\n \"completion_tokens\": 20,\n \"total_tokens\": 30,\n \"prompt_cache_hit_tokens\": 8,\n \"prompt_cache_miss_tokens\": 2\n },\n \"finish_reason\": \"stop\",\n \"tool_calls\": [\n {\n \"id\": \"call_abc123\",\n \"type\": \"function\",\n \"function\": { \"name\": \"fn_name\", \"arguments\": \"{\\\"key\\\":\\\"value\\\"}\" }\n }\n ],\n \"cost_usd\": 0.000012,\n \"session_id\": \"my-session\"\n}\n```\n\n## Tool: deepseek_fim\n\nFill-in-the-Middle (FIM) completion for code and content infilling. Provide a prefix (`prompt`) and an optional `suffix`; the model completes the text between them. Runs on DeepSeek's Beta completions endpoint in non-thinking mode; output is capped at 4096 tokens. Same cache-aware cost tracking and model fallback (v4-flash <-> v4-pro) as deepseek_chat. The deprecated deepseek-chat / deepseek-reasoner aliases are still accepted and resolve to v4-flash (FIM has no thinking mode).\n\nParameters: `prompt` (required), `suffix`, `model` (default deepseek-v4-flash), `max_tokens` (<= 4096), `temperature`, `stop` (string or array of up to 16).\n\n```json\n{\n \"prompt\": \"def fib(n):\\n if n < 2:\\n return n\\n return \",\n \"suffix\": \"\\n\\nprint(fib(10))\",\n \"model\": \"deepseek-v4-flash\",\n \"max_tokens\": 64\n}\n```\n\nResponse structuredContent: `text`, `model`, `usage`, `finish_reason`, `cost_usd` (and `routed_from` when an alias was used). Available on both the npm/stdio server and the hosted worker endpoint.\n\n## Tool: deepseek_sessions\n\nManage conversation sessions:\n\n```json\n{\"action\": \"list\"} // List all active sessions\n{\"action\": \"delete\", \"session_id\": \"my-session\"} // Delete specific session\n{\"action\": \"clear\"} // Clear all sessions\n```\n\n## MCP Resources\n\nThree read-only resources:\n\n| URI | Description |\n|-----|-------------|\n| deepseek://models | Model list with capabilities, context limits, pricing |\n| deepseek://config | Current server config (API key masked: sk-****1234) |\n| deepseek://usage | Real-time usage stats (requests, tokens, costs, sessions, cache ratio) |\n\n## Model Fallback & Circuit Breaker\n\n**Fallback**: On retryable errors (429, 503, timeout), automatically tries the other model:\n- deepseek-v4-flash fails → tries deepseek-v4-pro\n- deepseek-v4-pro fails → tries deepseek-v4-flash\n- The deprecated aliases (which resolve to v4-flash) fall back to deepseek-v4-pro\n- Response includes fallback info when fallback was used\n\n**Circuit Breaker**: Protects against cascading failures:\n- CLOSED → normal operation\n- After CIRCUIT_BREAKER_THRESHOLD failures (default 5) → OPEN (fast-fail for CIRCUIT_BREAKER_RESET_TIMEOUT ms)\n- After timeout → HALF_OPEN (probe with 1 request)\n- Probe succeeds → CLOSED; Probe fails → OPEN again\n\nDisable with FALLBACK_ENABLED=false.\n\n## Function Calling Flow\n\n1. Send messages with `tools` array defining available functions\n2. Model responds with `tool_calls` containing function name and arguments\n3. Execute the function locally\n4. Send result back as a `tool` role message with `tool_call_id`\n5. Model generates final response using the tool result\n\n## Cost Tracking\n\nV4 provides cache-aware pricing. Cost display shows:\n- Total cost in USD\n- Cache hit ratio (percentage of prompt tokens served from cache)\n- Estimated savings from cache hits\n\nExample output: `$0.0042 (cache hit: 80%, saved ~$0.0168)`\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-v4-flash | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n| CIRCUIT_BREAKER_THRESHOLD | 5 | Consecutive failures before circuit opens |\n| CIRCUIT_BREAKER_RESET_TIMEOUT | 30000 | Milliseconds before circuit half-opens |\n| MAX_SESSION_MESSAGES | 200 | Max messages per session (sliding window) |\n| RESPONSE_SCHEMA_MAX_RETRIES | 2 | Repair retries when a response_schema validation fails (0 disables) |\n\n## Prompt Templates (12 total)\n\n### Core Reasoning\n- debug_with_reasoning(code, error?, language?)\n- code_review_deep(code, language?, focus: security|performance|quality|all)\n- research_synthesis(topic, context?, depth: brief|moderate|comprehensive)\n- strategic_planning(goal, context?, constraints?)\n- explain_like_im_five(topic, audience: child|beginner|intermediate)\n\n### Advanced\n- mathematical_proof(statement, context?)\n- argument_validation(argument, type: informal|formal|both)\n- creative_ideation(challenge, constraints?, quantity: 1-20)\n- cost_comparison(task, estimated_tokens)\n- pair_programming(task, language, style: beginner|intermediate|expert)\n\n### Function Calling\n- function_call_debug(tools_json, messages_json, error?)\n- create_function_schema(description, examples?)\n\n## Remote Endpoint (Hosted)\n\nA hosted BYOK endpoint is available at: https://deepseek-mcp.tahirl.com/mcp\n\nSend your DeepSeek API key as `Authorization: Bearer `. No server-side key stored. Powered by Cloudflare Workers (global edge, zero cold start, free tier).\n\nEndpoints:\n- `GET /health` — health check (status, version, transport, timestamp)\n- `GET /` — server info (name, version, description, endpoints)\n- `POST /mcp` — MCP JSON-RPC requests (requires Bearer auth)\n\n## HTTP Transport (Self-Hosted)\n\nRun your own HTTP server instead of stdio:\n\n```bash\nTRANSPORT=http HTTP_PORT=3000 DEEPSEEK_API_KEY=your-key node dist/index.js\n```\n\nEndpoints:\n- `GET /health` — health check (status, version, uptime), always open\n- `POST /mcp` — MCP JSON-RPC requests (Streamable HTTP protocol)\n- `GET /mcp` — SSE stream (requires Mcp-Session-Id header)\n- `DELETE /mcp` — terminate session\n\nSecurity (1.8.0+): in HTTP mode the server holds DEEPSEEK_API_KEY and spends it on every deepseek_chat call, so the endpoint must not sit open on a public interface. HTTP_HOST defaults to 127.0.0.1, so a plain run only listens on loopback with DNS rebinding protection active. To accept remote connections set HTTP_HOST=0.0.0.0 and set HTTP_AUTH_TOKEN, which makes /mcp require `Authorization: Bearer ` (/health stays open). Binding to 0.0.0.0 without a token prints a startup warning. Use HTTP_ALLOWED_HOSTS to keep host-header validation when binding to all interfaces, and front internet-facing deployments with an authenticating TLS reverse proxy.\n\nEach MCP session gets its own McpServer instance AND its own SessionStore instance (session-store isolation added in 1.7.0 to prevent cross-session data exposure). DeepSeekClient is shared across sessions since it is a stateless API client.\n\n## Docker\n\n```bash\ndocker build -t deepseek-mcp-server .\n# Reachable from the host's loopback only, with a bearer token\ndocker run -d -p 127.0.0.1:3000:3000 -e DEEPSEEK_API_KEY=your-key -e HTTP_AUTH_TOKEN=your-token deepseek-mcp-server\n```\n\nDocker defaults to HTTP transport and binds 0.0.0.0 inside the container (required for the port mapping). Control exposure at the publish layer: the bundled docker-compose.yml publishes to 127.0.0.1 only. Set HTTP_AUTH_TOKEN if you publish the port on a public interface. Health check is built in (wget to /health every 30s).\n\n## Architecture\n\n```\nsrc/\n index.ts # Entry point, bootstrap\n server.ts # McpServer factory (version from package.json)\n deepseek-client.ts # DeepSeek API wrapper (circuit breaker + fallback)\n config.ts # Zod-validated config from env vars\n cost.ts # V4 cache-aware cost calculation\n schemas.ts # Zod input validation schemas\n types.ts # TypeScript types + type guards\n errors.ts # Custom error classes\n session.ts # In-memory session store\n circuit-breaker.ts # Circuit breaker pattern\n usage-tracker.ts # Usage statistics tracker\n transport-http.ts # Streamable HTTP transport (Express)\n tools/\n deepseek-chat.ts # deepseek_chat tool (sessions + fallback)\n deepseek-fim.ts # deepseek_fim tool (fill-in-the-middle)\n deepseek-sessions.ts # deepseek_sessions tool\n index.ts # Tool registration aggregator\n resources/\n models.ts # deepseek://models resource\n config.ts # deepseek://config resource\n usage.ts # deepseek://usage resource\n index.ts # Resource registration aggregator\n prompts/\n core.ts # 5 core reasoning prompts\n advanced.ts # 5 advanced prompts\n function-calling.ts # 2 function calling prompts\n index.ts # Prompt registration aggregator\n```\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-v4-flash | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n| CIRCUIT_BREAKER_THRESHOLD | 5 | Consecutive failures before circuit opens |\n| CIRCUIT_BREAKER_RESET_TIMEOUT | 30000 | Milliseconds before circuit half-opens |\n| MAX_SESSION_MESSAGES | 200 | Max messages per session (sliding window) |\n| RESPONSE_SCHEMA_MAX_RETRIES | 2 | Repair retries when a response_schema validation fails (0 disables) |\n| ENABLE_MULTIMODAL | false | Enable multimodal (image) input support |\n| TRANSPORT | stdio | Transport mode: stdio or http |\n| HTTP_PORT | 3000 | HTTP server port (when TRANSPORT=http) |\n| HTTP_HOST | 127.0.0.1 | Bind address for HTTP transport. Loopback by default; set to 0.0.0.0 for remote access (pair with auth) |\n| HTTP_AUTH_TOKEN | (unset) | When set, /mcp requires `Authorization: Bearer `. /health stays open |\n| HTTP_ALLOWED_HOSTS | (unset) | Comma-separated allowed Host headers for DNS rebinding protection when binding to 0.0.0.0 |\n\n## Tech Stack\n\n- TypeScript 6.0 (strict mode)\n- @modelcontextprotocol/sdk for MCP protocol\n- OpenAI SDK v6 for API compatibility\n- Zod v4 for validation\n- Vitest for testing (280 tests, ~89% line coverage)\n- Node.js 20+\n\n## Links\n\n- npm: https://www.npmjs.com/package/@arikusi/deepseek-mcp-server\n- GitHub: https://github.com/arikusi/deepseek-mcp-server\n- DeepSeek API: https://api-docs.deepseek.com\n- MCP Spec: https://modelcontextprotocol.io\n"}, "a2a5885e42975938baa142244bca442862bb8679": {"t": 1785749498.606406, "n": 19583, "h": "03524dba43fc965b", "c": "# DeepSeek MCP Server - Complete Documentation\n\nA Model Context Protocol (MCP) server that integrates DeepSeek V4 models (deepseek-v4-flash and deepseek-v4-pro, 1M context) with MCP-compatible clients like Claude Code and Gemini CLI. deepseek-chat and deepseek-reasoner remain as compatibility aliases. Hosted remote endpoint at deepseek-mcp.tahirl.com/mcp with BYOK (Bring Your Own Key) authentication. Also supports local stdio and self-hosted Streamable HTTP transports with Docker deployment. Provides multi-turn sessions, model fallback with circuit breaker, MCP Resources, chat completion, thinking mode, JSON output, function calling, multimodal content support, model-aware cost tracking, and 12 prompt templates.\n\n## Installation\n\n### Remote (No Install)\n\n```bash\n# Claude Code — connect to hosted endpoint with your own API key\nclaude mcp add --transport http deepseek \\\n https://deepseek-mcp.tahirl.com/mcp \\\n --header \"Authorization: Bearer YOUR_DEEPSEEK_API_KEY\"\n```\n\n```json\n// Cursor / Windsurf / VS Code\n{\n \"mcpServers\": {\n \"deepseek\": {\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\",\n \"headers\": { \"Authorization\": \"Bearer ${DEEPSEEK_API_KEY}\" }\n }\n }\n}\n```\n\n### Local (stdio)\n\n```bash\n# Claude Code (all projects)\nclaude mcp add -s user deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Gemini CLI\ngemini mcp add deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Global npm install\nnpm install -g @arikusi/deepseek-mcp-server\n\n# Docker (build locally, bind to loopback with a token)\ndocker build -t deepseek-mcp-server . && docker run -d -p 127.0.0.1:3000:3000 -e DEEPSEEK_API_KEY=your-key -e HTTP_AUTH_TOKEN=your-token deepseek-mcp-server\n```\n\nGet API key: https://platform.deepseek.com\n\n## Models\n\ndeepseek-v4-flash and deepseek-v4-pro are the live V4 models (1M context, up to 384K output). deepseek-chat and deepseek-reasoner are accepted as compatibility aliases that resolve to deepseek-v4-flash; the DeepSeek API retires those two names on 2026-07-24. All calls are non-thinking by default for fast responses; enable reasoning with thinking:{type:\"enabled\"} or the deepseek-reasoner alias.\n\n### deepseek-v4-flash (default)\n- Fast, economical. General conversations, coding, content generation, agent loops\n- 1M context, up to 384K max output\n- Supports thinking mode, JSON mode, function calling\n- Pricing: $0.0028/1M cache hit, $0.14/1M cache miss, $0.28/1M output\n\n### deepseek-v4-pro\n- Most capable. Complex reasoning, math, hard multi-step tasks\n- 1M context, up to 384K max output\n- Supports thinking mode, JSON mode, function calling\n- Pricing: $0.003625/1M cache hit, $0.435/1M cache miss, $0.87/1M output\n\n### Model Routing Table\n\n| User selects | Sent to API | Thinking | reasoning_content |\n|---|---|---|---|\n| deepseek-v4-flash | deepseek-v4-flash | Off (default) | No |\n| deepseek-v4-flash + thinking:enabled | deepseek-v4-flash | On | Yes |\n| deepseek-v4-pro | deepseek-v4-pro | Off (default) | No |\n| deepseek-chat (alias) | deepseek-v4-flash | Off | No |\n| deepseek-reasoner (alias) | deepseek-v4-flash | On | Yes |\n\n## Tool: deepseek_chat\n\n### Input Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| messages | Array<{role, content, tool_call_id?}> | Yes | - | Conversation messages. Roles: system, user, assistant, tool. Content can be string or array of content parts (text/image_url) when multimodal is enabled |\n| model | \"deepseek-v4-flash\" \\| \"deepseek-v4-pro\" \\| \"deepseek-chat\" \\| \"deepseek-reasoner\" | No | \"deepseek-v4-flash\" | Model to use (chat/reasoner are aliases for v4-flash) |\n| temperature | number (0-2) | No | 1.0 | Sampling temperature. Ignored when thinking enabled |\n| max_tokens | number (1-384000) | No | model default | Max output tokens (V4 supports up to 384000; 1M context) |\n| stream | boolean | No | false | Enable streaming mode |\n| tools | Array (max 128) | No | - | Function calling tool definitions |\n| tool_choice | \"auto\" \\| \"none\" \\| \"required\" \\| {type, function} | No | \"auto\" | Which tool to call |\n| thinking | {type: \"enabled\" \\| \"disabled\"} | No | disabled | Toggle thinking mode (non-thinking by default) |\n| reasoning_effort | \"high\" \\| \"max\" | No | high | Reasoning effort while thinking is active |\n| json_mode | boolean | No | false | Enable JSON output mode (supported by both models) |\n| response_schema | object (JSON Schema) | No | - | Validate the model output against this JSON Schema. Implies JSON output. On mismatch, up to RESPONSE_SCHEMA_MAX_RETRIES repair retries feed the error back to the model. Schema regex patterns are ReDoS-screened; an unsafe pattern is rejected |\n| session_id | string | No | - | Session ID for multi-turn conversations |\n\n### Multi-Turn Sessions\n\nUse `session_id` to maintain conversation context across requests:\n\n```json\n// First request - creates session\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What is the capital of France?\"}],\n \"session_id\": \"my-session\"\n}\n\n// Second request - previous context is automatically prepended\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What about Germany?\"}],\n \"session_id\": \"my-session\"\n}\n```\n\nSessions are stored in memory. In STDIO transport they live for the lifetime of the server process. In HTTP transport each MCP session gets its own isolated SessionStore, so session_id values are scoped to the MCP session that created them and are not visible to other connected HTTP clients. Sessions expire after SESSION_TTL_MINUTES (default: 30). Omit session_id for stateless single-turn requests.\n\n### Thinking Mode\n\nThere are two ways to enable thinking mode:\n\n1. Select deepseek-reasoner (automatically routed as chat + thinking):\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Analyze quicksort complexity\"}],\n \"model\": \"deepseek-reasoner\"\n}\n```\n\n2. Explicitly enable thinking on deepseek-chat:\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Analyze quicksort complexity\"}],\n \"model\": \"deepseek-chat\",\n \"thinking\": {\"type\": \"enabled\"}\n}\n```\n\nBoth produce identical results. When thinking is active, temperature/top_p/frequency_penalty/presence_penalty are automatically ignored. The response includes a reasoning_content field with the chain-of-thought.\n\n### JSON Output Mode\n\nGet structured JSON responses:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Return a json object with user data\"}],\n \"model\": \"deepseek-chat\",\n \"json_mode\": true\n}\n```\n\nInclude the word \"json\" in your prompt for best results. Supported by both models. When JSON output is requested, the returned content is deterministically recovered as a clean JSON value even if the model wrapped it in a code fence or leaked chain-of-thought around it (no extra API call); if nothing parseable is found the raw text is preserved and json_parse_error is set.\n\n### Schema-Validated JSON\n\nPass a response_schema (a JSON Schema) to constrain the output shape, not just its syntax:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Classify this sentiment as json: \\\"loved it\\\"\"}],\n \"model\": \"deepseek-v4-flash\",\n \"response_schema\": {\n \"type\": \"object\",\n \"properties\": {\n \"sentiment\": {\"type\": \"string\", \"enum\": [\"positive\", \"negative\", \"neutral\"]},\n \"confidence\": {\"type\": \"number\", \"minimum\": 0, \"maximum\": 1}\n },\n \"required\": [\"sentiment\", \"confidence\"],\n \"additionalProperties\": false\n }\n}\n```\n\nThe server validates the parsed output with Ajv. On a mismatch it issues up to RESPONSE_SCHEMA_MAX_RETRIES repair retries (default 2, set 0 for a single deterministic call), feeding the validation error back to the model, and returns the first schema-valid object. A persistent mismatch surfaces as structuredContent.schema = {valid: false, attempts, error} rather than a silently coerced answer. Regex patterns in the schema are screened for catastrophic backtracking (ReDoS) before compilation; an unsafe pattern such as ^(a+)+$ is rejected up front as an invalid schema so it can never block the event loop.\n\n### Tool Definition Format\n\n```json\n{\n \"type\": \"function\",\n \"function\": {\n \"name\": \"function_name\",\n \"description\": \"What the function does\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"param1\": { \"type\": \"string\", \"description\": \"...\" }\n },\n \"required\": [\"param1\"]\n }\n }\n}\n```\n\n### Response Format\n\n```json\n{\n \"content\": \"Response text\",\n \"reasoning_content\": \"Chain-of-thought (reasoner only)\",\n \"model\": \"deepseek-chat\",\n \"usage\": {\n \"prompt_tokens\": 10,\n \"completion_tokens\": 20,\n \"total_tokens\": 30,\n \"prompt_cache_hit_tokens\": 8,\n \"prompt_cache_miss_tokens\": 2\n },\n \"finish_reason\": \"stop\",\n \"tool_calls\": [\n {\n \"id\": \"call_abc123\",\n \"type\": \"function\",\n \"function\": { \"name\": \"fn_name\", \"arguments\": \"{\\\"key\\\":\\\"value\\\"}\" }\n }\n ],\n \"cost_usd\": 0.000012,\n \"session_id\": \"my-session\"\n}\n```\n\n## Tool: deepseek_fim\n\nFill-in-the-Middle (FIM) completion for code and content infilling. Provide a prefix (`prompt`) and an optional `suffix`; the model completes the text between them. Runs on DeepSeek's Beta completions endpoint in non-thinking mode; output is capped at 4096 tokens. Same cache-aware cost tracking and model fallback (v4-flash <-> v4-pro) as deepseek_chat. The deepseek-chat / deepseek-reasoner aliases resolve to v4-flash (FIM has no thinking mode).\n\nParameters: `prompt` (required), `suffix`, `model` (default deepseek-v4-flash), `max_tokens` (<= 4096), `temperature`, `stop` (string or array of up to 16).\n\n```json\n{\n \"prompt\": \"def fib(n):\\n if n < 2:\\n return n\\n return \",\n \"suffix\": \"\\n\\nprint(fib(10))\",\n \"model\": \"deepseek-v4-flash\",\n \"max_tokens\": 64\n}\n```\n\nResponse structuredContent: `text`, `model`, `usage`, `finish_reason`, `cost_usd` (and `routed_from` when an alias was used). Available on both the npm/stdio server and the hosted worker endpoint.\n\n## Tool: deepseek_sessions\n\nManage conversation sessions:\n\n```json\n{\"action\": \"list\"} // List all active sessions\n{\"action\": \"delete\", \"session_id\": \"my-session\"} // Delete specific session\n{\"action\": \"clear\"} // Clear all sessions\n```\n\n## MCP Resources\n\nThree read-only resources:\n\n| URI | Description |\n|-----|-------------|\n| deepseek://models | Model list with capabilities, context limits, pricing |\n| deepseek://config | Current server config (API key masked: sk-****1234) |\n| deepseek://usage | Real-time usage stats (requests, tokens, costs, sessions, cache ratio) |\n\n## Model Fallback & Circuit Breaker\n\n**Fallback**: On retryable errors (429, 503, timeout), automatically tries the other model:\n- deepseek-chat fails → tries deepseek-reasoner\n- deepseek-reasoner fails → tries deepseek-chat\n- Response includes fallback info when fallback was used\n\n**Circuit Breaker**: Protects against cascading failures:\n- CLOSED → normal operation\n- After CIRCUIT_BREAKER_THRESHOLD failures (default 5) → OPEN (fast-fail for CIRCUIT_BREAKER_RESET_TIMEOUT ms)\n- After timeout → HALF_OPEN (probe with 1 request)\n- Probe succeeds → CLOSED; Probe fails → OPEN again\n\nDisable with FALLBACK_ENABLED=false.\n\n## Function Calling Flow\n\n1. Send messages with `tools` array defining available functions\n2. Model responds with `tool_calls` containing function name and arguments\n3. Execute the function locally\n4. Send result back as a `tool` role message with `tool_call_id`\n5. Model generates final response using the tool result\n\n## Cost Tracking\n\nV4 provides cache-aware pricing. Cost display shows:\n- Total cost in USD\n- Cache hit ratio (percentage of prompt tokens served from cache)\n- Estimated savings from cache hits\n\nExample output: `$0.0042 (cache hit: 80%, saved ~$0.0168)`\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-v4-flash | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n| CIRCUIT_BREAKER_THRESHOLD | 5 | Consecutive failures before circuit opens |\n| CIRCUIT_BREAKER_RESET_TIMEOUT | 30000 | Milliseconds before circuit half-opens |\n| MAX_SESSION_MESSAGES | 200 | Max messages per session (sliding window) |\n| RESPONSE_SCHEMA_MAX_RETRIES | 2 | Repair retries when a response_schema validation fails (0 disables) |\n\n## Prompt Templates (12 total)\n\n### Core Reasoning\n- debug_with_reasoning(code, error?, language?)\n- code_review_deep(code, language?, focus: security|performance|quality|all)\n- research_synthesis(topic, context?, depth: brief|moderate|comprehensive)\n- strategic_planning(goal, context?, constraints?)\n- explain_like_im_five(topic, audience: child|beginner|intermediate)\n\n### Advanced\n- mathematical_proof(statement, context?)\n- argument_validation(argument, type: informal|formal|both)\n- creative_ideation(challenge, constraints?, quantity: 1-20)\n- cost_comparison(task, estimated_tokens)\n- pair_programming(task, language, style: beginner|intermediate|expert)\n\n### Function Calling\n- function_call_debug(tools_json, messages_json, error?)\n- create_function_schema(description, examples?)\n\n## Remote Endpoint (Hosted)\n\nA hosted BYOK endpoint is available at: https://deepseek-mcp.tahirl.com/mcp\n\nSend your DeepSeek API key as `Authorization: Bearer `. No server-side key stored. Powered by Cloudflare Workers (global edge, zero cold start, free tier).\n\nEndpoints:\n- `GET /health` — health check (status, version, transport, timestamp)\n- `GET /` — server info (name, version, description, endpoints)\n- `POST /mcp` — MCP JSON-RPC requests (requires Bearer auth)\n\n## HTTP Transport (Self-Hosted)\n\nRun your own HTTP server instead of stdio:\n\n```bash\nTRANSPORT=http HTTP_PORT=3000 DEEPSEEK_API_KEY=your-key node dist/index.js\n```\n\nEndpoints:\n- `GET /health` — health check (status, version, uptime), always open\n- `POST /mcp` — MCP JSON-RPC requests (Streamable HTTP protocol)\n- `GET /mcp` — SSE stream (requires Mcp-Session-Id header)\n- `DELETE /mcp` — terminate session\n\nSecurity (1.8.0+): in HTTP mode the server holds DEEPSEEK_API_KEY and spends it on every deepseek_chat call, so the endpoint must not sit open on a public interface. HTTP_HOST defaults to 127.0.0.1, so a plain run only listens on loopback with DNS rebinding protection active. To accept remote connections set HTTP_HOST=0.0.0.0 and set HTTP_AUTH_TOKEN, which makes /mcp require `Authorization: Bearer ` (/health stays open). Binding to 0.0.0.0 without a token prints a startup warning. Use HTTP_ALLOWED_HOSTS to keep host-header validation when binding to all interfaces, and front internet-facing deployments with an authenticating TLS reverse proxy.\n\nEach MCP session gets its own McpServer instance AND its own SessionStore instance (session-store isolation added in 1.7.0 to prevent cross-session data exposure). DeepSeekClient is shared across sessions since it is a stateless API client.\n\n## Docker\n\n```bash\ndocker build -t deepseek-mcp-server .\n# Reachable from the host's loopback only, with a bearer token\ndocker run -d -p 127.0.0.1:3000:3000 -e DEEPSEEK_API_KEY=your-key -e HTTP_AUTH_TOKEN=your-token deepseek-mcp-server\n```\n\nDocker defaults to HTTP transport and binds 0.0.0.0 inside the container (required for the port mapping). Control exposure at the publish layer: the bundled docker-compose.yml publishes to 127.0.0.1 only. Set HTTP_AUTH_TOKEN if you publish the port on a public interface. Health check is built in (wget to /health every 30s).\n\n## Architecture\n\n```\nsrc/\n index.ts # Entry point, bootstrap\n server.ts # McpServer factory (version from package.json)\n deepseek-client.ts # DeepSeek API wrapper (circuit breaker + fallback)\n config.ts # Zod-validated config from env vars\n cost.ts # V4 cache-aware cost calculation\n schemas.ts # Zod input validation schemas\n types.ts # TypeScript types + type guards\n errors.ts # Custom error classes\n session.ts # In-memory session store\n circuit-breaker.ts # Circuit breaker pattern\n usage-tracker.ts # Usage statistics tracker\n transport-http.ts # Streamable HTTP transport (Express)\n tools/\n deepseek-chat.ts # deepseek_chat tool (sessions + fallback)\n deepseek-fim.ts # deepseek_fim tool (fill-in-the-middle)\n deepseek-sessions.ts # deepseek_sessions tool\n index.ts # Tool registration aggregator\n resources/\n models.ts # deepseek://models resource\n config.ts # deepseek://config resource\n usage.ts # deepseek://usage resource\n index.ts # Resource registration aggregator\n prompts/\n core.ts # 5 core reasoning prompts\n advanced.ts # 5 advanced prompts\n function-calling.ts # 2 function calling prompts\n index.ts # Prompt registration aggregator\n```\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-v4-flash | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n| CIRCUIT_BREAKER_THRESHOLD | 5 | Consecutive failures before circuit opens |\n| CIRCUIT_BREAKER_RESET_TIMEOUT | 30000 | Milliseconds before circuit half-opens |\n| MAX_SESSION_MESSAGES | 200 | Max messages per session (sliding window) |\n| RESPONSE_SCHEMA_MAX_RETRIES | 2 | Repair retries when a response_schema validation fails (0 disables) |\n| ENABLE_MULTIMODAL | false | Enable multimodal (image) input support |\n| TRANSPORT | stdio | Transport mode: stdio or http |\n| HTTP_PORT | 3000 | HTTP server port (when TRANSPORT=http) |\n| HTTP_HOST | 127.0.0.1 | Bind address for HTTP transport. Loopback by default; set to 0.0.0.0 for remote access (pair with auth) |\n| HTTP_AUTH_TOKEN | (unset) | When set, /mcp requires `Authorization: Bearer `. /health stays open |\n| HTTP_ALLOWED_HOSTS | (unset) | Comma-separated allowed Host headers for DNS rebinding protection when binding to 0.0.0.0 |\n\n## Tech Stack\n\n- TypeScript 6.0 (strict mode)\n- @modelcontextprotocol/sdk for MCP protocol\n- OpenAI SDK v6 for API compatibility\n- Zod v4 for validation\n- Vitest for testing (280 tests, ~89% line coverage)\n- Node.js 20+\n\n## Links\n\n- npm: https://www.npmjs.com/package/@arikusi/deepseek-mcp-server\n- GitHub: https://github.com/arikusi/deepseek-mcp-server\n- DeepSeek API: https://api-docs.deepseek.com\n- MCP Spec: https://modelcontextprotocol.io\n"}, "f54b61e09e3baf2233e65c66c830ff43d4a87d46": {"t": 1785749499.8238413, "n": 17603, "h": "a651f0c2f6e0fa14", "c": "# DeepSeek MCP Server - Complete Documentation\n\nA Model Context Protocol (MCP) server that integrates DeepSeek V4 models (deepseek-v4-flash and deepseek-v4-pro, 1M context) with MCP-compatible clients like Claude Code and Gemini CLI. deepseek-chat and deepseek-reasoner remain as compatibility aliases. Hosted remote endpoint at deepseek-mcp.tahirl.com/mcp with BYOK (Bring Your Own Key) authentication. Also supports local stdio and self-hosted Streamable HTTP transports with Docker deployment. Provides multi-turn sessions, model fallback with circuit breaker, MCP Resources, chat completion, thinking mode, JSON output, function calling, multimodal content support, model-aware cost tracking, and 12 prompt templates.\n\n## Installation\n\n### Remote (No Install)\n\n```bash\n# Claude Code — connect to hosted endpoint with your own API key\nclaude mcp add --transport http deepseek \\\n https://deepseek-mcp.tahirl.com/mcp \\\n --header \"Authorization: Bearer YOUR_DEEPSEEK_API_KEY\"\n```\n\n```json\n// Cursor / Windsurf / VS Code\n{\n \"mcpServers\": {\n \"deepseek\": {\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\",\n \"headers\": { \"Authorization\": \"Bearer ${DEEPSEEK_API_KEY}\" }\n }\n }\n}\n```\n\n### Local (stdio)\n\n```bash\n# Claude Code (all projects)\nclaude mcp add -s user deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Gemini CLI\ngemini mcp add deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Global npm install\nnpm install -g @arikusi/deepseek-mcp-server\n\n# Docker (build locally, bind to loopback with a token)\ndocker build -t deepseek-mcp-server . && docker run -d -p 127.0.0.1:3000:3000 -e DEEPSEEK_API_KEY=your-key -e HTTP_AUTH_TOKEN=your-token deepseek-mcp-server\n```\n\nGet API key: https://platform.deepseek.com\n\n## Models\n\ndeepseek-v4-flash and deepseek-v4-pro are the live V4 models (1M context, up to 384K output). deepseek-chat and deepseek-reasoner are accepted as compatibility aliases that resolve to deepseek-v4-flash; the DeepSeek API retires those two names on 2026-07-24. All calls are non-thinking by default for fast responses; enable reasoning with thinking:{type:\"enabled\"} or the deepseek-reasoner alias.\n\n### deepseek-v4-flash (default)\n- Fast, economical. General conversations, coding, content generation, agent loops\n- 1M context, up to 384K max output\n- Supports thinking mode, JSON mode, function calling\n- Pricing: $0.0028/1M cache hit, $0.14/1M cache miss, $0.28/1M output\n\n### deepseek-v4-pro\n- Most capable. Complex reasoning, math, hard multi-step tasks\n- 1M context, up to 384K max output\n- Supports thinking mode, JSON mode, function calling\n- Pricing: $0.003625/1M cache hit, $0.435/1M cache miss, $0.87/1M output\n\n### Model Routing Table\n\n| User selects | Sent to API | Thinking | reasoning_content |\n|---|---|---|---|\n| deepseek-v4-flash | deepseek-v4-flash | Off (default) | No |\n| deepseek-v4-flash + thinking:enabled | deepseek-v4-flash | On | Yes |\n| deepseek-v4-pro | deepseek-v4-pro | Off (default) | No |\n| deepseek-chat (alias) | deepseek-v4-flash | Off | No |\n| deepseek-reasoner (alias) | deepseek-v4-flash | On | Yes |\n\n## Tool: deepseek_chat\n\n### Input Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| messages | Array<{role, content, tool_call_id?}> | Yes | - | Conversation messages. Roles: system, user, assistant, tool. Content can be string or array of content parts (text/image_url) when multimodal is enabled |\n| model | \"deepseek-v4-flash\" \\| \"deepseek-v4-pro\" \\| \"deepseek-chat\" \\| \"deepseek-reasoner\" | No | \"deepseek-v4-flash\" | Model to use (chat/reasoner are aliases for v4-flash) |\n| temperature | number (0-2) | No | 1.0 | Sampling temperature. Ignored when thinking enabled |\n| max_tokens | number (1-384000) | No | model default | Max output tokens (V4 supports up to 384000; 1M context) |\n| stream | boolean | No | false | Enable streaming mode |\n| tools | Array (max 128) | No | - | Function calling tool definitions |\n| tool_choice | \"auto\" \\| \"none\" \\| \"required\" \\| {type, function} | No | \"auto\" | Which tool to call |\n| thinking | {type: \"enabled\" \\| \"disabled\"} | No | disabled | Toggle thinking mode (non-thinking by default) |\n| reasoning_effort | \"high\" \\| \"max\" | No | high | Reasoning effort while thinking is active |\n| json_mode | boolean | No | false | Enable JSON output mode (supported by both models) |\n| session_id | string | No | - | Session ID for multi-turn conversations |\n\n### Multi-Turn Sessions\n\nUse `session_id` to maintain conversation context across requests:\n\n```json\n// First request - creates session\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What is the capital of France?\"}],\n \"session_id\": \"my-session\"\n}\n\n// Second request - previous context is automatically prepended\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What about Germany?\"}],\n \"session_id\": \"my-session\"\n}\n```\n\nSessions are stored in memory. In STDIO transport they live for the lifetime of the server process. In HTTP transport each MCP session gets its own isolated SessionStore, so session_id values are scoped to the MCP session that created them and are not visible to other connected HTTP clients. Sessions expire after SESSION_TTL_MINUTES (default: 30). Omit session_id for stateless single-turn requests.\n\n### Thinking Mode\n\nThere are two ways to enable thinking mode:\n\n1. Select deepseek-reasoner (automatically routed as chat + thinking):\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Analyze quicksort complexity\"}],\n \"model\": \"deepseek-reasoner\"\n}\n```\n\n2. Explicitly enable thinking on deepseek-chat:\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Analyze quicksort complexity\"}],\n \"model\": \"deepseek-chat\",\n \"thinking\": {\"type\": \"enabled\"}\n}\n```\n\nBoth produce identical results. When thinking is active, temperature/top_p/frequency_penalty/presence_penalty are automatically ignored. The response includes a reasoning_content field with the chain-of-thought.\n\n### JSON Output Mode\n\nGet structured JSON responses:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Return a json object with user data\"}],\n \"model\": \"deepseek-chat\",\n \"json_mode\": true\n}\n```\n\nInclude the word \"json\" in your prompt for best results. Supported by both models.\n\n### Tool Definition Format\n\n```json\n{\n \"type\": \"function\",\n \"function\": {\n \"name\": \"function_name\",\n \"description\": \"What the function does\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"param1\": { \"type\": \"string\", \"description\": \"...\" }\n },\n \"required\": [\"param1\"]\n }\n }\n}\n```\n\n### Response Format\n\n```json\n{\n \"content\": \"Response text\",\n \"reasoning_content\": \"Chain-of-thought (reasoner only)\",\n \"model\": \"deepseek-chat\",\n \"usage\": {\n \"prompt_tokens\": 10,\n \"completion_tokens\": 20,\n \"total_tokens\": 30,\n \"prompt_cache_hit_tokens\": 8,\n \"prompt_cache_miss_tokens\": 2\n },\n \"finish_reason\": \"stop\",\n \"tool_calls\": [\n {\n \"id\": \"call_abc123\",\n \"type\": \"function\",\n \"function\": { \"name\": \"fn_name\", \"arguments\": \"{\\\"key\\\":\\\"value\\\"}\" }\n }\n ],\n \"cost_usd\": 0.000012,\n \"session_id\": \"my-session\"\n}\n```\n\n## Tool: deepseek_fim\n\nFill-in-the-Middle (FIM) completion for code and content infilling. Provide a prefix (`prompt`) and an optional `suffix`; the model completes the text between them. Runs on DeepSeek's Beta completions endpoint in non-thinking mode; output is capped at 4096 tokens. Same cache-aware cost tracking and model fallback (v4-flash <-> v4-pro) as deepseek_chat. The deepseek-chat / deepseek-reasoner aliases resolve to v4-flash (FIM has no thinking mode).\n\nParameters: `prompt` (required), `suffix`, `model` (default deepseek-v4-flash), `max_tokens` (<= 4096), `temperature`, `stop` (string or array of up to 16).\n\n```json\n{\n \"prompt\": \"def fib(n):\\n if n < 2:\\n return n\\n return \",\n \"suffix\": \"\\n\\nprint(fib(10))\",\n \"model\": \"deepseek-v4-flash\",\n \"max_tokens\": 64\n}\n```\n\nResponse structuredContent: `text`, `model`, `usage`, `finish_reason`, `cost_usd` (and `routed_from` when an alias was used). Available on both the npm/stdio server and the hosted worker endpoint.\n\n## Tool: deepseek_sessions\n\nManage conversation sessions:\n\n```json\n{\"action\": \"list\"} // List all active sessions\n{\"action\": \"delete\", \"session_id\": \"my-session\"} // Delete specific session\n{\"action\": \"clear\"} // Clear all sessions\n```\n\n## MCP Resources\n\nThree read-only resources:\n\n| URI | Description |\n|-----|-------------|\n| deepseek://models | Model list with capabilities, context limits, pricing |\n| deepseek://config | Current server config (API key masked: sk-****1234) |\n| deepseek://usage | Real-time usage stats (requests, tokens, costs, sessions, cache ratio) |\n\n## Model Fallback & Circuit Breaker\n\n**Fallback**: On retryable errors (429, 503, timeout), automatically tries the other model:\n- deepseek-chat fails → tries deepseek-reasoner\n- deepseek-reasoner fails → tries deepseek-chat\n- Response includes fallback info when fallback was used\n\n**Circuit Breaker**: Protects against cascading failures:\n- CLOSED → normal operation\n- After CIRCUIT_BREAKER_THRESHOLD failures (default 5) → OPEN (fast-fail for CIRCUIT_BREAKER_RESET_TIMEOUT ms)\n- After timeout → HALF_OPEN (probe with 1 request)\n- Probe succeeds → CLOSED; Probe fails → OPEN again\n\nDisable with FALLBACK_ENABLED=false.\n\n## Function Calling Flow\n\n1. Send messages with `tools` array defining available functions\n2. Model responds with `tool_calls` containing function name and arguments\n3. Execute the function locally\n4. Send result back as a `tool` role message with `tool_call_id`\n5. Model generates final response using the tool result\n\n## Cost Tracking\n\nV4 provides cache-aware pricing. Cost display shows:\n- Total cost in USD\n- Cache hit ratio (percentage of prompt tokens served from cache)\n- Estimated savings from cache hits\n\nExample output: `$0.0042 (cache hit: 80%, saved ~$0.0168)`\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-v4-flash | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n| CIRCUIT_BREAKER_THRESHOLD | 5 | Consecutive failures before circuit opens |\n| CIRCUIT_BREAKER_RESET_TIMEOUT | 30000 | Milliseconds before circuit half-opens |\n| MAX_SESSION_MESSAGES | 200 | Max messages per session (sliding window) |\n\n## Prompt Templates (12 total)\n\n### Core Reasoning\n- debug_with_reasoning(code, error?, language?)\n- code_review_deep(code, language?, focus: security|performance|quality|all)\n- research_synthesis(topic, context?, depth: brief|moderate|comprehensive)\n- strategic_planning(goal, context?, constraints?)\n- explain_like_im_five(topic, audience: child|beginner|intermediate)\n\n### Advanced\n- mathematical_proof(statement, context?)\n- argument_validation(argument, type: informal|formal|both)\n- creative_ideation(challenge, constraints?, quantity: 1-20)\n- cost_comparison(task, estimated_tokens)\n- pair_programming(task, language, style: beginner|intermediate|expert)\n\n### Function Calling\n- function_call_debug(tools_json, messages_json, error?)\n- create_function_schema(description, examples?)\n\n## Remote Endpoint (Hosted)\n\nA hosted BYOK endpoint is available at: https://deepseek-mcp.tahirl.com/mcp\n\nSend your DeepSeek API key as `Authorization: Bearer `. No server-side key stored. Powered by Cloudflare Workers (global edge, zero cold start, free tier).\n\nEndpoints:\n- `GET /health` — health check (status, version, transport, timestamp)\n- `GET /` — server info (name, version, description, endpoints)\n- `POST /mcp` — MCP JSON-RPC requests (requires Bearer auth)\n\n## HTTP Transport (Self-Hosted)\n\nRun your own HTTP server instead of stdio:\n\n```bash\nTRANSPORT=http HTTP_PORT=3000 DEEPSEEK_API_KEY=your-key node dist/index.js\n```\n\nEndpoints:\n- `GET /health` — health check (status, version, uptime), always open\n- `POST /mcp` — MCP JSON-RPC requests (Streamable HTTP protocol)\n- `GET /mcp` — SSE stream (requires Mcp-Session-Id header)\n- `DELETE /mcp` — terminate session\n\nSecurity (1.8.0+): in HTTP mode the server holds DEEPSEEK_API_KEY and spends it on every deepseek_chat call, so the endpoint must not sit open on a public interface. HTTP_HOST defaults to 127.0.0.1, so a plain run only listens on loopback with DNS rebinding protection active. To accept remote connections set HTTP_HOST=0.0.0.0 and set HTTP_AUTH_TOKEN, which makes /mcp require `Authorization: Bearer ` (/health stays open). Binding to 0.0.0.0 without a token prints a startup warning. Use HTTP_ALLOWED_HOSTS to keep host-header validation when binding to all interfaces, and front internet-facing deployments with an authenticating TLS reverse proxy.\n\nEach MCP session gets its own McpServer instance AND its own SessionStore instance (session-store isolation added in 1.7.0 to prevent cross-session data exposure). DeepSeekClient is shared across sessions since it is a stateless API client.\n\n## Docker\n\n```bash\ndocker build -t deepseek-mcp-server .\n# Reachable from the host's loopback only, with a bearer token\ndocker run -d -p 127.0.0.1:3000:3000 -e DEEPSEEK_API_KEY=your-key -e HTTP_AUTH_TOKEN=your-token deepseek-mcp-server\n```\n\nDocker defaults to HTTP transport and binds 0.0.0.0 inside the container (required for the port mapping). Control exposure at the publish layer: the bundled docker-compose.yml publishes to 127.0.0.1 only. Set HTTP_AUTH_TOKEN if you publish the port on a public interface. Health check is built in (wget to /health every 30s).\n\n## Architecture\n\n```\nsrc/\n index.ts # Entry point, bootstrap\n server.ts # McpServer factory (version from package.json)\n deepseek-client.ts # DeepSeek API wrapper (circuit breaker + fallback)\n config.ts # Zod-validated config from env vars\n cost.ts # V4 cache-aware cost calculation\n schemas.ts # Zod input validation schemas\n types.ts # TypeScript types + type guards\n errors.ts # Custom error classes\n session.ts # In-memory session store\n circuit-breaker.ts # Circuit breaker pattern\n usage-tracker.ts # Usage statistics tracker\n transport-http.ts # Streamable HTTP transport (Express)\n tools/\n deepseek-chat.ts # deepseek_chat tool (sessions + fallback)\n deepseek-fim.ts # deepseek_fim tool (fill-in-the-middle)\n deepseek-sessions.ts # deepseek_sessions tool\n index.ts # Tool registration aggregator\n resources/\n models.ts # deepseek://models resource\n config.ts # deepseek://config resource\n usage.ts # deepseek://usage resource\n index.ts # Resource registration aggregator\n prompts/\n core.ts # 5 core reasoning prompts\n advanced.ts # 5 advanced prompts\n function-calling.ts # 2 function calling prompts\n index.ts # Prompt registration aggregator\n```\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-v4-flash | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n| CIRCUIT_BREAKER_THRESHOLD | 5 | Consecutive failures before circuit opens |\n| CIRCUIT_BREAKER_RESET_TIMEOUT | 30000 | Milliseconds before circuit half-opens |\n| MAX_SESSION_MESSAGES | 200 | Max messages per session (sliding window) |\n| ENABLE_MULTIMODAL | false | Enable multimodal (image) input support |\n| TRANSPORT | stdio | Transport mode: stdio or http |\n| HTTP_PORT | 3000 | HTTP server port (when TRANSPORT=http) |\n| HTTP_HOST | 127.0.0.1 | Bind address for HTTP transport. Loopback by default; set to 0.0.0.0 for remote access (pair with auth) |\n| HTTP_AUTH_TOKEN | (unset) | When set, /mcp requires `Authorization: Bearer `. /health stays open |\n| HTTP_ALLOWED_HOSTS | (unset) | Comma-separated allowed Host headers for DNS rebinding protection when binding to 0.0.0.0 |\n\n## Tech Stack\n\n- TypeScript 6.0 (strict mode)\n- @modelcontextprotocol/sdk for MCP protocol\n- OpenAI SDK v6 for API compatibility\n- Zod v4 for validation\n- Vitest for testing (280 tests, ~89% line coverage)\n- Node.js 20+\n\n## Links\n\n- npm: https://www.npmjs.com/package/@arikusi/deepseek-mcp-server\n- GitHub: https://github.com/arikusi/deepseek-mcp-server\n- DeepSeek API: https://api-docs.deepseek.com\n- MCP Spec: https://modelcontextprotocol.io\n"}, "c06a79b0b0c950af15b930e9d69b4a02262ae17b": {"t": 1785749501.0459845, "n": 16534, "h": "f1c557644dff0157", "c": "# DeepSeek MCP Server - Complete Documentation\n\nA Model Context Protocol (MCP) server that integrates DeepSeek V4 models (deepseek-v4-flash and deepseek-v4-pro, 1M context) with MCP-compatible clients like Claude Code and Gemini CLI. deepseek-chat and deepseek-reasoner remain as compatibility aliases. Hosted remote endpoint at deepseek-mcp.tahirl.com/mcp with BYOK (Bring Your Own Key) authentication. Also supports local stdio and self-hosted Streamable HTTP transports with Docker deployment. Provides multi-turn sessions, model fallback with circuit breaker, MCP Resources, chat completion, thinking mode, JSON output, function calling, multimodal content support, model-aware cost tracking, and 12 prompt templates.\n\n## Installation\n\n### Remote (No Install)\n\n```bash\n# Claude Code — connect to hosted endpoint with your own API key\nclaude mcp add --transport http deepseek \\\n https://deepseek-mcp.tahirl.com/mcp \\\n --header \"Authorization: Bearer YOUR_DEEPSEEK_API_KEY\"\n```\n\n```json\n// Cursor / Windsurf / VS Code\n{\n \"mcpServers\": {\n \"deepseek\": {\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\",\n \"headers\": { \"Authorization\": \"Bearer ${DEEPSEEK_API_KEY}\" }\n }\n }\n}\n```\n\n### Local (stdio)\n\n```bash\n# Claude Code (all projects)\nclaude mcp add -s user deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Gemini CLI\ngemini mcp add deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Global npm install\nnpm install -g @arikusi/deepseek-mcp-server\n\n# Docker (build locally, bind to loopback with a token)\ndocker build -t deepseek-mcp-server . && docker run -d -p 127.0.0.1:3000:3000 -e DEEPSEEK_API_KEY=your-key -e HTTP_AUTH_TOKEN=your-token deepseek-mcp-server\n```\n\nGet API key: https://platform.deepseek.com\n\n## Models\n\ndeepseek-v4-flash and deepseek-v4-pro are the live V4 models (1M context, up to 384K output). deepseek-chat and deepseek-reasoner are accepted as compatibility aliases that resolve to deepseek-v4-flash; the DeepSeek API retires those two names on 2026-07-24. All calls are non-thinking by default for fast responses; enable reasoning with thinking:{type:\"enabled\"} or the deepseek-reasoner alias.\n\n### deepseek-v4-flash (default)\n- Fast, economical. General conversations, coding, content generation, agent loops\n- 1M context, up to 384K max output\n- Supports thinking mode, JSON mode, function calling\n- Pricing: $0.0028/1M cache hit, $0.14/1M cache miss, $0.28/1M output\n\n### deepseek-v4-pro\n- Most capable. Complex reasoning, math, hard multi-step tasks\n- 1M context, up to 384K max output\n- Supports thinking mode, JSON mode, function calling\n- Pricing: $0.003625/1M cache hit, $0.435/1M cache miss, $0.87/1M output\n\n### Model Routing Table\n\n| User selects | Sent to API | Thinking | reasoning_content |\n|---|---|---|---|\n| deepseek-v4-flash | deepseek-v4-flash | Off (default) | No |\n| deepseek-v4-flash + thinking:enabled | deepseek-v4-flash | On | Yes |\n| deepseek-v4-pro | deepseek-v4-pro | Off (default) | No |\n| deepseek-chat (alias) | deepseek-v4-flash | Off | No |\n| deepseek-reasoner (alias) | deepseek-v4-flash | On | Yes |\n\n## Tool: deepseek_chat\n\n### Input Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| messages | Array<{role, content, tool_call_id?}> | Yes | - | Conversation messages. Roles: system, user, assistant, tool. Content can be string or array of content parts (text/image_url) when multimodal is enabled |\n| model | \"deepseek-v4-flash\" \\| \"deepseek-v4-pro\" \\| \"deepseek-chat\" \\| \"deepseek-reasoner\" | No | \"deepseek-v4-flash\" | Model to use (chat/reasoner are aliases for v4-flash) |\n| temperature | number (0-2) | No | 1.0 | Sampling temperature. Ignored when thinking enabled |\n| max_tokens | number (1-384000) | No | model default | Max output tokens (V4 supports up to 384000; 1M context) |\n| stream | boolean | No | false | Enable streaming mode |\n| tools | Array (max 128) | No | - | Function calling tool definitions |\n| tool_choice | \"auto\" \\| \"none\" \\| \"required\" \\| {type, function} | No | \"auto\" | Which tool to call |\n| thinking | {type: \"enabled\" \\| \"disabled\"} | No | disabled | Toggle thinking mode (non-thinking by default) |\n| reasoning_effort | \"high\" \\| \"max\" | No | high | Reasoning effort while thinking is active |\n| json_mode | boolean | No | false | Enable JSON output mode (supported by both models) |\n| session_id | string | No | - | Session ID for multi-turn conversations |\n\n### Multi-Turn Sessions\n\nUse `session_id` to maintain conversation context across requests:\n\n```json\n// First request - creates session\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What is the capital of France?\"}],\n \"session_id\": \"my-session\"\n}\n\n// Second request - previous context is automatically prepended\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What about Germany?\"}],\n \"session_id\": \"my-session\"\n}\n```\n\nSessions are stored in memory. In STDIO transport they live for the lifetime of the server process. In HTTP transport each MCP session gets its own isolated SessionStore, so session_id values are scoped to the MCP session that created them and are not visible to other connected HTTP clients. Sessions expire after SESSION_TTL_MINUTES (default: 30). Omit session_id for stateless single-turn requests.\n\n### Thinking Mode\n\nThere are two ways to enable thinking mode:\n\n1. Select deepseek-reasoner (automatically routed as chat + thinking):\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Analyze quicksort complexity\"}],\n \"model\": \"deepseek-reasoner\"\n}\n```\n\n2. Explicitly enable thinking on deepseek-chat:\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Analyze quicksort complexity\"}],\n \"model\": \"deepseek-chat\",\n \"thinking\": {\"type\": \"enabled\"}\n}\n```\n\nBoth produce identical results. When thinking is active, temperature/top_p/frequency_penalty/presence_penalty are automatically ignored. The response includes a reasoning_content field with the chain-of-thought.\n\n### JSON Output Mode\n\nGet structured JSON responses:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Return a json object with user data\"}],\n \"model\": \"deepseek-chat\",\n \"json_mode\": true\n}\n```\n\nInclude the word \"json\" in your prompt for best results. Supported by both models.\n\n### Tool Definition Format\n\n```json\n{\n \"type\": \"function\",\n \"function\": {\n \"name\": \"function_name\",\n \"description\": \"What the function does\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"param1\": { \"type\": \"string\", \"description\": \"...\" }\n },\n \"required\": [\"param1\"]\n }\n }\n}\n```\n\n### Response Format\n\n```json\n{\n \"content\": \"Response text\",\n \"reasoning_content\": \"Chain-of-thought (reasoner only)\",\n \"model\": \"deepseek-chat\",\n \"usage\": {\n \"prompt_tokens\": 10,\n \"completion_tokens\": 20,\n \"total_tokens\": 30,\n \"prompt_cache_hit_tokens\": 8,\n \"prompt_cache_miss_tokens\": 2\n },\n \"finish_reason\": \"stop\",\n \"tool_calls\": [\n {\n \"id\": \"call_abc123\",\n \"type\": \"function\",\n \"function\": { \"name\": \"fn_name\", \"arguments\": \"{\\\"key\\\":\\\"value\\\"}\" }\n }\n ],\n \"cost_usd\": 0.000012,\n \"session_id\": \"my-session\"\n}\n```\n\n## Tool: deepseek_sessions\n\nManage conversation sessions:\n\n```json\n{\"action\": \"list\"} // List all active sessions\n{\"action\": \"delete\", \"session_id\": \"my-session\"} // Delete specific session\n{\"action\": \"clear\"} // Clear all sessions\n```\n\n## MCP Resources\n\nThree read-only resources:\n\n| URI | Description |\n|-----|-------------|\n| deepseek://models | Model list with capabilities, context limits, pricing |\n| deepseek://config | Current server config (API key masked: sk-****1234) |\n| deepseek://usage | Real-time usage stats (requests, tokens, costs, sessions, cache ratio) |\n\n## Model Fallback & Circuit Breaker\n\n**Fallback**: On retryable errors (429, 503, timeout), automatically tries the other model:\n- deepseek-chat fails → tries deepseek-reasoner\n- deepseek-reasoner fails → tries deepseek-chat\n- Response includes fallback info when fallback was used\n\n**Circuit Breaker**: Protects against cascading failures:\n- CLOSED → normal operation\n- After CIRCUIT_BREAKER_THRESHOLD failures (default 5) → OPEN (fast-fail for CIRCUIT_BREAKER_RESET_TIMEOUT ms)\n- After timeout → HALF_OPEN (probe with 1 request)\n- Probe succeeds → CLOSED; Probe fails → OPEN again\n\nDisable with FALLBACK_ENABLED=false.\n\n## Function Calling Flow\n\n1. Send messages with `tools` array defining available functions\n2. Model responds with `tool_calls` containing function name and arguments\n3. Execute the function locally\n4. Send result back as a `tool` role message with `tool_call_id`\n5. Model generates final response using the tool result\n\n## Cost Tracking\n\nV4 provides cache-aware pricing. Cost display shows:\n- Total cost in USD\n- Cache hit ratio (percentage of prompt tokens served from cache)\n- Estimated savings from cache hits\n\nExample output: `$0.0042 (cache hit: 80%, saved ~$0.0168)`\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-v4-flash | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n| CIRCUIT_BREAKER_THRESHOLD | 5 | Consecutive failures before circuit opens |\n| CIRCUIT_BREAKER_RESET_TIMEOUT | 30000 | Milliseconds before circuit half-opens |\n| MAX_SESSION_MESSAGES | 200 | Max messages per session (sliding window) |\n\n## Prompt Templates (12 total)\n\n### Core Reasoning\n- debug_with_reasoning(code, error?, language?)\n- code_review_deep(code, language?, focus: security|performance|quality|all)\n- research_synthesis(topic, context?, depth: brief|moderate|comprehensive)\n- strategic_planning(goal, context?, constraints?)\n- explain_like_im_five(topic, audience: child|beginner|intermediate)\n\n### Advanced\n- mathematical_proof(statement, context?)\n- argument_validation(argument, type: informal|formal|both)\n- creative_ideation(challenge, constraints?, quantity: 1-20)\n- cost_comparison(task, estimated_tokens)\n- pair_programming(task, language, style: beginner|intermediate|expert)\n\n### Function Calling\n- function_call_debug(tools_json, messages_json, error?)\n- create_function_schema(description, examples?)\n\n## Remote Endpoint (Hosted)\n\nA hosted BYOK endpoint is available at: https://deepseek-mcp.tahirl.com/mcp\n\nSend your DeepSeek API key as `Authorization: Bearer `. No server-side key stored. Powered by Cloudflare Workers (global edge, zero cold start, free tier).\n\nEndpoints:\n- `GET /health` — health check (status, version, transport, timestamp)\n- `GET /` — server info (name, version, description, endpoints)\n- `POST /mcp` — MCP JSON-RPC requests (requires Bearer auth)\n\n## HTTP Transport (Self-Hosted)\n\nRun your own HTTP server instead of stdio:\n\n```bash\nTRANSPORT=http HTTP_PORT=3000 DEEPSEEK_API_KEY=your-key node dist/index.js\n```\n\nEndpoints:\n- `GET /health` — health check (status, version, uptime), always open\n- `POST /mcp` — MCP JSON-RPC requests (Streamable HTTP protocol)\n- `GET /mcp` — SSE stream (requires Mcp-Session-Id header)\n- `DELETE /mcp` — terminate session\n\nSecurity (1.8.0+): in HTTP mode the server holds DEEPSEEK_API_KEY and spends it on every deepseek_chat call, so the endpoint must not sit open on a public interface. HTTP_HOST defaults to 127.0.0.1, so a plain run only listens on loopback with DNS rebinding protection active. To accept remote connections set HTTP_HOST=0.0.0.0 and set HTTP_AUTH_TOKEN, which makes /mcp require `Authorization: Bearer ` (/health stays open). Binding to 0.0.0.0 without a token prints a startup warning. Use HTTP_ALLOWED_HOSTS to keep host-header validation when binding to all interfaces, and front internet-facing deployments with an authenticating TLS reverse proxy.\n\nEach MCP session gets its own McpServer instance AND its own SessionStore instance (session-store isolation added in 1.7.0 to prevent cross-session data exposure). DeepSeekClient is shared across sessions since it is a stateless API client.\n\n## Docker\n\n```bash\ndocker build -t deepseek-mcp-server .\n# Reachable from the host's loopback only, with a bearer token\ndocker run -d -p 127.0.0.1:3000:3000 -e DEEPSEEK_API_KEY=your-key -e HTTP_AUTH_TOKEN=your-token deepseek-mcp-server\n```\n\nDocker defaults to HTTP transport and binds 0.0.0.0 inside the container (required for the port mapping). Control exposure at the publish layer: the bundled docker-compose.yml publishes to 127.0.0.1 only. Set HTTP_AUTH_TOKEN if you publish the port on a public interface. Health check is built in (wget to /health every 30s).\n\n## Architecture\n\n```\nsrc/\n index.ts # Entry point, bootstrap\n server.ts # McpServer factory (version from package.json)\n deepseek-client.ts # DeepSeek API wrapper (circuit breaker + fallback)\n config.ts # Zod-validated config from env vars\n cost.ts # V4 cache-aware cost calculation\n schemas.ts # Zod input validation schemas\n types.ts # TypeScript types + type guards\n errors.ts # Custom error classes\n session.ts # In-memory session store\n circuit-breaker.ts # Circuit breaker pattern\n usage-tracker.ts # Usage statistics tracker\n transport-http.ts # Streamable HTTP transport (Express)\n tools/\n deepseek-chat.ts # deepseek_chat tool (sessions + fallback)\n deepseek-sessions.ts # deepseek_sessions tool\n index.ts # Tool registration aggregator\n resources/\n models.ts # deepseek://models resource\n config.ts # deepseek://config resource\n usage.ts # deepseek://usage resource\n index.ts # Resource registration aggregator\n prompts/\n core.ts # 5 core reasoning prompts\n advanced.ts # 5 advanced prompts\n function-calling.ts # 2 function calling prompts\n index.ts # Prompt registration aggregator\n```\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-v4-flash | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n| CIRCUIT_BREAKER_THRESHOLD | 5 | Consecutive failures before circuit opens |\n| CIRCUIT_BREAKER_RESET_TIMEOUT | 30000 | Milliseconds before circuit half-opens |\n| MAX_SESSION_MESSAGES | 200 | Max messages per session (sliding window) |\n| ENABLE_MULTIMODAL | false | Enable multimodal (image) input support |\n| TRANSPORT | stdio | Transport mode: stdio or http |\n| HTTP_PORT | 3000 | HTTP server port (when TRANSPORT=http) |\n| HTTP_HOST | 127.0.0.1 | Bind address for HTTP transport. Loopback by default; set to 0.0.0.0 for remote access (pair with auth) |\n| HTTP_AUTH_TOKEN | (unset) | When set, /mcp requires `Authorization: Bearer `. /health stays open |\n| HTTP_ALLOWED_HOSTS | (unset) | Comma-separated allowed Host headers for DNS rebinding protection when binding to 0.0.0.0 |\n\n## Tech Stack\n\n- TypeScript 6.0 (strict mode)\n- @modelcontextprotocol/sdk for MCP protocol\n- OpenAI SDK v6 for API compatibility\n- Zod v4 for validation\n- Vitest for testing (280 tests, ~89% line coverage)\n- Node.js 20+\n\n## Links\n\n- npm: https://www.npmjs.com/package/@arikusi/deepseek-mcp-server\n- GitHub: https://github.com/arikusi/deepseek-mcp-server\n- DeepSeek API: https://api-docs.deepseek.com\n- MCP Spec: https://modelcontextprotocol.io\n"}, "dab07ed93ddde0ab219d4cb7066785847db53a32": {"t": 1785749502.8708417, "n": 16155, "h": "09f6d6ca55216003", "c": "# DeepSeek MCP Server - Complete Documentation\n\nA Model Context Protocol (MCP) server that integrates DeepSeek AI models with MCP-compatible clients like Claude Code and Gemini CLI. Hosted remote endpoint at deepseek-mcp.tahirl.com/mcp with BYOK (Bring Your Own Key) authentication. Also supports local stdio and self-hosted Streamable HTTP transports with Docker deployment. Provides multi-turn sessions, model fallback with circuit breaker, MCP Resources, chat completion, thinking mode, JSON output, function calling, multimodal content support, model-aware cost tracking, and 12 prompt templates.\n\n## Installation\n\n### Remote (No Install)\n\n```bash\n# Claude Code — connect to hosted endpoint with your own API key\nclaude mcp add --transport http deepseek \\\n https://deepseek-mcp.tahirl.com/mcp \\\n --header \"Authorization: Bearer YOUR_DEEPSEEK_API_KEY\"\n```\n\n```json\n// Cursor / Windsurf / VS Code\n{\n \"mcpServers\": {\n \"deepseek\": {\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\",\n \"headers\": { \"Authorization\": \"Bearer ${DEEPSEEK_API_KEY}\" }\n }\n }\n}\n```\n\n### Local (stdio)\n\n```bash\n# Claude Code (all projects)\nclaude mcp add -s user deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Gemini CLI\ngemini mcp add deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Global npm install\nnpm install -g @arikusi/deepseek-mcp-server\n\n# Docker\ndocker run -d -p 3000:3000 -e DEEPSEEK_API_KEY=your-key ghcr.io/arikusi/deepseek-mcp-server\n```\n\nGet API key: https://platform.deepseek.com\n\n## Models\n\nBoth models run DeepSeek-V3.2 with unified pricing. They are the same underlying model with different modes:\n- deepseek-chat = V3.2 non-thinking mode\n- deepseek-reasoner = V3.2 thinking mode (transparently routed as deepseek-chat + thinking:{type:\"enabled\"})\n\n### deepseek-chat\n- General conversations, coding, content generation\n- Non-thinking mode by default. Can enable thinking via `thinking: {type: \"enabled\"}` parameter\n- 128K context, 8K max output (default 4K), 64K max output when thinking enabled\n- Supports thinking mode, JSON mode, function calling, FIM completion\n- Pricing: $0.028/1M cache hit, $0.28/1M cache miss, $0.42/1M output\n\n### deepseek-reasoner\n- Complex reasoning, math, logic, multi-step tasks\n- Transparently routed as deepseek-chat with thinking:{type:\"enabled\"}\n- This enables full feature parity including function calling, which the raw deepseek-reasoner API does not support\n- 128K context, 64K max output (default 32K)\n- Supports JSON mode, function calling (via transparent routing)\n- Pricing: $0.028/1M cache hit, $0.28/1M cache miss, $0.42/1M output\n\n### Model Routing Table\n\n| User selects | Sent to API | Thinking | reasoning_content | Function calling |\n|---|---|---|---|---|\n| deepseek-chat | deepseek-chat | Off | No | Yes |\n| deepseek-chat + thinking:enabled | deepseek-chat + thinking | On | Yes | Yes |\n| deepseek-reasoner | deepseek-chat + thinking | On | Yes | Yes |\n\n## Tool: deepseek_chat\n\n### Input Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| messages | Array<{role, content, tool_call_id?}> | Yes | - | Conversation messages. Roles: system, user, assistant, tool. Content can be string or array of content parts (text/image_url) when multimodal is enabled |\n| model | \"deepseek-chat\" \\| \"deepseek-reasoner\" | No | \"deepseek-chat\" | Model to use |\n| temperature | number (0-2) | No | 1.0 | Sampling temperature. Ignored when thinking enabled |\n| max_tokens | number (1-65536) | No | model max | Max tokens: chat=8192, reasoner=65536. Both have 128K context |\n| stream | boolean | No | false | Enable streaming mode |\n| tools | Array (max 128) | No | - | Function calling tool definitions |\n| tool_choice | \"auto\" \\| \"none\" \\| \"required\" \\| {type, function} | No | \"auto\" | Which tool to call |\n| thinking | {type: \"enabled\" \\| \"disabled\"} | No | - | Enable thinking mode |\n| json_mode | boolean | No | false | Enable JSON output mode (supported by both models) |\n| session_id | string | No | - | Session ID for multi-turn conversations |\n\n### Multi-Turn Sessions\n\nUse `session_id` to maintain conversation context across requests:\n\n```json\n// First request - creates session\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What is the capital of France?\"}],\n \"session_id\": \"my-session\"\n}\n\n// Second request - previous context is automatically prepended\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What about Germany?\"}],\n \"session_id\": \"my-session\"\n}\n```\n\nSessions are stored in memory. In STDIO transport they live for the lifetime of the server process. In HTTP transport each MCP session gets its own isolated SessionStore, so session_id values are scoped to the MCP session that created them and are not visible to other connected HTTP clients. Sessions expire after SESSION_TTL_MINUTES (default: 30). Omit session_id for stateless single-turn requests.\n\n### Thinking Mode\n\nThere are two ways to enable thinking mode:\n\n1. Select deepseek-reasoner (automatically routed as chat + thinking):\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Analyze quicksort complexity\"}],\n \"model\": \"deepseek-reasoner\"\n}\n```\n\n2. Explicitly enable thinking on deepseek-chat:\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Analyze quicksort complexity\"}],\n \"model\": \"deepseek-chat\",\n \"thinking\": {\"type\": \"enabled\"}\n}\n```\n\nBoth produce identical results. When thinking is active, temperature/top_p/frequency_penalty/presence_penalty are automatically ignored. The response includes a reasoning_content field with the chain-of-thought.\n\n### JSON Output Mode\n\nGet structured JSON responses:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Return a json object with user data\"}],\n \"model\": \"deepseek-chat\",\n \"json_mode\": true\n}\n```\n\nInclude the word \"json\" in your prompt for best results. Supported by both models.\n\n### Tool Definition Format\n\n```json\n{\n \"type\": \"function\",\n \"function\": {\n \"name\": \"function_name\",\n \"description\": \"What the function does\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"param1\": { \"type\": \"string\", \"description\": \"...\" }\n },\n \"required\": [\"param1\"]\n }\n }\n}\n```\n\n### Response Format\n\n```json\n{\n \"content\": \"Response text\",\n \"reasoning_content\": \"Chain-of-thought (reasoner only)\",\n \"model\": \"deepseek-chat\",\n \"usage\": {\n \"prompt_tokens\": 10,\n \"completion_tokens\": 20,\n \"total_tokens\": 30,\n \"prompt_cache_hit_tokens\": 8,\n \"prompt_cache_miss_tokens\": 2\n },\n \"finish_reason\": \"stop\",\n \"tool_calls\": [\n {\n \"id\": \"call_abc123\",\n \"type\": \"function\",\n \"function\": { \"name\": \"fn_name\", \"arguments\": \"{\\\"key\\\":\\\"value\\\"}\" }\n }\n ],\n \"cost_usd\": 0.000012,\n \"session_id\": \"my-session\"\n}\n```\n\n## Tool: deepseek_sessions\n\nManage conversation sessions:\n\n```json\n{\"action\": \"list\"} // List all active sessions\n{\"action\": \"delete\", \"session_id\": \"my-session\"} // Delete specific session\n{\"action\": \"clear\"} // Clear all sessions\n```\n\n## MCP Resources\n\nThree read-only resources:\n\n| URI | Description |\n|-----|-------------|\n| deepseek://models | Model list with capabilities, context limits, pricing |\n| deepseek://config | Current server config (API key masked: sk-****1234) |\n| deepseek://usage | Real-time usage stats (requests, tokens, costs, sessions, cache ratio) |\n\n## Model Fallback & Circuit Breaker\n\n**Fallback**: On retryable errors (429, 503, timeout), automatically tries the other model:\n- deepseek-chat fails → tries deepseek-reasoner\n- deepseek-reasoner fails → tries deepseek-chat\n- Response includes fallback info when fallback was used\n\n**Circuit Breaker**: Protects against cascading failures:\n- CLOSED → normal operation\n- After CIRCUIT_BREAKER_THRESHOLD failures (default 5) → OPEN (fast-fail for CIRCUIT_BREAKER_RESET_TIMEOUT ms)\n- After timeout → HALF_OPEN (probe with 1 request)\n- Probe succeeds → CLOSED; Probe fails → OPEN again\n\nDisable with FALLBACK_ENABLED=false.\n\n## Function Calling Flow\n\n1. Send messages with `tools` array defining available functions\n2. Model responds with `tool_calls` containing function name and arguments\n3. Execute the function locally\n4. Send result back as a `tool` role message with `tool_call_id`\n5. Model generates final response using the tool result\n\n## Cost Tracking\n\nV3.2 provides cache-aware pricing. Cost display shows:\n- Total cost in USD\n- Cache hit ratio (percentage of prompt tokens served from cache)\n- Estimated savings from cache hits\n\nExample output: `$0.0042 (cache hit: 80%, saved ~$0.0168)`\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-chat | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n| CIRCUIT_BREAKER_THRESHOLD | 5 | Consecutive failures before circuit opens |\n| CIRCUIT_BREAKER_RESET_TIMEOUT | 30000 | Milliseconds before circuit half-opens |\n| MAX_SESSION_MESSAGES | 200 | Max messages per session (sliding window) |\n\n## Prompt Templates (12 total)\n\n### Core Reasoning\n- debug_with_reasoning(code, error?, language?)\n- code_review_deep(code, language?, focus: security|performance|quality|all)\n- research_synthesis(topic, context?, depth: brief|moderate|comprehensive)\n- strategic_planning(goal, context?, constraints?)\n- explain_like_im_five(topic, audience: child|beginner|intermediate)\n\n### Advanced\n- mathematical_proof(statement, context?)\n- argument_validation(argument, type: informal|formal|both)\n- creative_ideation(challenge, constraints?, quantity: 1-20)\n- cost_comparison(task, estimated_tokens)\n- pair_programming(task, language, style: beginner|intermediate|expert)\n\n### Function Calling\n- function_call_debug(tools_json, messages_json, error?)\n- create_function_schema(description, examples?)\n\n## Remote Endpoint (Hosted)\n\nA hosted BYOK endpoint is available at: https://deepseek-mcp.tahirl.com/mcp\n\nSend your DeepSeek API key as `Authorization: Bearer `. No server-side key stored. Powered by Cloudflare Workers (global edge, zero cold start, free tier).\n\nEndpoints:\n- `GET /health` — health check (status, version, transport, timestamp)\n- `GET /` — server info (name, version, description, endpoints)\n- `POST /mcp` — MCP JSON-RPC requests (requires Bearer auth)\n\n## HTTP Transport (Self-Hosted)\n\nRun your own HTTP server instead of stdio:\n\n```bash\nTRANSPORT=http HTTP_PORT=3000 DEEPSEEK_API_KEY=your-key node dist/index.js\n```\n\nEndpoints:\n- `GET /health` — health check (status, version, uptime), always open\n- `POST /mcp` — MCP JSON-RPC requests (Streamable HTTP protocol)\n- `GET /mcp` — SSE stream (requires Mcp-Session-Id header)\n- `DELETE /mcp` — terminate session\n\nSecurity (1.8.0+): in HTTP mode the server holds DEEPSEEK_API_KEY and spends it on every deepseek_chat call, so the endpoint must not sit open on a public interface. HTTP_HOST defaults to 127.0.0.1, so a plain run only listens on loopback with DNS rebinding protection active. To accept remote connections set HTTP_HOST=0.0.0.0 and set HTTP_AUTH_TOKEN, which makes /mcp require `Authorization: Bearer ` (/health stays open). Binding to 0.0.0.0 without a token prints a startup warning. Use HTTP_ALLOWED_HOSTS to keep host-header validation when binding to all interfaces, and front internet-facing deployments with an authenticating TLS reverse proxy.\n\nEach MCP session gets its own McpServer instance AND its own SessionStore instance (session-store isolation added in 1.7.0 to prevent cross-session data exposure). DeepSeekClient is shared across sessions since it is a stateless API client.\n\n## Docker\n\n```bash\ndocker build -t deepseek-mcp-server .\n# Reachable from the host's loopback only, with a bearer token\ndocker run -d -p 127.0.0.1:3000:3000 -e DEEPSEEK_API_KEY=your-key -e HTTP_AUTH_TOKEN=your-token deepseek-mcp-server\n```\n\nDocker defaults to HTTP transport and binds 0.0.0.0 inside the container (required for the port mapping). Control exposure at the publish layer: the bundled docker-compose.yml publishes to 127.0.0.1 only. Set HTTP_AUTH_TOKEN if you publish the port on a public interface. Health check is built in (wget to /health every 30s).\n\n## Architecture\n\n```\nsrc/\n index.ts # Entry point, bootstrap\n server.ts # McpServer factory (version from package.json)\n deepseek-client.ts # DeepSeek API wrapper (circuit breaker + fallback)\n config.ts # Zod-validated config from env vars\n cost.ts # V3.2 cache-aware cost calculation\n schemas.ts # Zod input validation schemas\n types.ts # TypeScript types + type guards\n errors.ts # Custom error classes\n session.ts # In-memory session store\n circuit-breaker.ts # Circuit breaker pattern\n usage-tracker.ts # Usage statistics tracker\n transport-http.ts # Streamable HTTP transport (Express)\n tools/\n deepseek-chat.ts # deepseek_chat tool (sessions + fallback)\n deepseek-sessions.ts # deepseek_sessions tool\n index.ts # Tool registration aggregator\n resources/\n models.ts # deepseek://models resource\n config.ts # deepseek://config resource\n usage.ts # deepseek://usage resource\n index.ts # Resource registration aggregator\n prompts/\n core.ts # 5 core reasoning prompts\n advanced.ts # 5 advanced prompts\n function-calling.ts # 2 function calling prompts\n index.ts # Prompt registration aggregator\n```\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-chat | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n| CIRCUIT_BREAKER_THRESHOLD | 5 | Consecutive failures before circuit opens |\n| CIRCUIT_BREAKER_RESET_TIMEOUT | 30000 | Milliseconds before circuit half-opens |\n| MAX_SESSION_MESSAGES | 200 | Max messages per session (sliding window) |\n| ENABLE_MULTIMODAL | false | Enable multimodal (image) input support |\n| TRANSPORT | stdio | Transport mode: stdio or http |\n| HTTP_PORT | 3000 | HTTP server port (when TRANSPORT=http) |\n| HTTP_HOST | 127.0.0.1 | Bind address for HTTP transport. Loopback by default; set to 0.0.0.0 for remote access (pair with auth) |\n| HTTP_AUTH_TOKEN | (unset) | When set, /mcp requires `Authorization: Bearer `. /health stays open |\n| HTTP_ALLOWED_HOSTS | (unset) | Comma-separated allowed Host headers for DNS rebinding protection when binding to 0.0.0.0 |\n\n## Tech Stack\n\n- TypeScript 5.7 (strict mode)\n- @modelcontextprotocol/sdk for MCP protocol\n- OpenAI SDK v6 for API compatibility\n- Zod v4 for validation\n- Vitest for testing (276 tests, ~89% line coverage)\n- Node.js 18+\n\n## Links\n\n- npm: https://www.npmjs.com/package/@arikusi/deepseek-mcp-server\n- GitHub: https://github.com/arikusi/deepseek-mcp-server\n- DeepSeek API: https://api-docs.deepseek.com\n- MCP Spec: https://modelcontextprotocol.io\n"}, "9fd514292d23d59ca1434b01f019aca6ef4356f9": {"t": 1785749504.0589962, "n": 14777, "h": "71cb0b968fadd906", "c": "# DeepSeek MCP Server - Complete Documentation\n\nA Model Context Protocol (MCP) server that integrates DeepSeek AI models with MCP-compatible clients like Claude Code and Gemini CLI. Hosted remote endpoint at deepseek-mcp.tahirl.com/mcp with BYOK (Bring Your Own Key) authentication. Also supports local stdio and self-hosted Streamable HTTP transports with Docker deployment. Provides multi-turn sessions, model fallback with circuit breaker, MCP Resources, chat completion, thinking mode, JSON output, function calling, multimodal content support, model-aware cost tracking, and 12 prompt templates.\n\n## Installation\n\n### Remote (No Install)\n\n```bash\n# Claude Code — connect to hosted endpoint with your own API key\nclaude mcp add --transport http deepseek \\\n https://deepseek-mcp.tahirl.com/mcp \\\n --header \"Authorization: Bearer YOUR_DEEPSEEK_API_KEY\"\n```\n\n```json\n// Cursor / Windsurf / VS Code\n{\n \"mcpServers\": {\n \"deepseek\": {\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\",\n \"headers\": { \"Authorization\": \"Bearer ${DEEPSEEK_API_KEY}\" }\n }\n }\n}\n```\n\n### Local (stdio)\n\n```bash\n# Claude Code (all projects)\nclaude mcp add -s user deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Gemini CLI\ngemini mcp add deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Global npm install\nnpm install -g @arikusi/deepseek-mcp-server\n\n# Docker\ndocker run -d -p 3000:3000 -e DEEPSEEK_API_KEY=your-key ghcr.io/arikusi/deepseek-mcp-server\n```\n\nGet API key: https://platform.deepseek.com\n\n## Models\n\nBoth models run DeepSeek-V3.2 with unified pricing. They are the same underlying model with different modes:\n- deepseek-chat = V3.2 non-thinking mode\n- deepseek-reasoner = V3.2 thinking mode (transparently routed as deepseek-chat + thinking:{type:\"enabled\"})\n\n### deepseek-chat\n- General conversations, coding, content generation\n- Non-thinking mode by default. Can enable thinking via `thinking: {type: \"enabled\"}` parameter\n- 128K context, 8K max output (default 4K), 64K max output when thinking enabled\n- Supports thinking mode, JSON mode, function calling, FIM completion\n- Pricing: $0.028/1M cache hit, $0.28/1M cache miss, $0.42/1M output\n\n### deepseek-reasoner\n- Complex reasoning, math, logic, multi-step tasks\n- Transparently routed as deepseek-chat with thinking:{type:\"enabled\"}\n- This enables full feature parity including function calling, which the raw deepseek-reasoner API does not support\n- 128K context, 64K max output (default 32K)\n- Supports JSON mode, function calling (via transparent routing)\n- Pricing: $0.028/1M cache hit, $0.28/1M cache miss, $0.42/1M output\n\n### Model Routing Table\n\n| User selects | Sent to API | Thinking | reasoning_content | Function calling |\n|---|---|---|---|---|\n| deepseek-chat | deepseek-chat | Off | No | Yes |\n| deepseek-chat + thinking:enabled | deepseek-chat + thinking | On | Yes | Yes |\n| deepseek-reasoner | deepseek-chat + thinking | On | Yes | Yes |\n\n## Tool: deepseek_chat\n\n### Input Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| messages | Array<{role, content, tool_call_id?}> | Yes | - | Conversation messages. Roles: system, user, assistant, tool. Content can be string or array of content parts (text/image_url) when multimodal is enabled |\n| model | \"deepseek-chat\" \\| \"deepseek-reasoner\" | No | \"deepseek-chat\" | Model to use |\n| temperature | number (0-2) | No | 1.0 | Sampling temperature. Ignored when thinking enabled |\n| max_tokens | number (1-65536) | No | model max | Max tokens: chat=8192, reasoner=65536. Both have 128K context |\n| stream | boolean | No | false | Enable streaming mode |\n| tools | Array (max 128) | No | - | Function calling tool definitions |\n| tool_choice | \"auto\" \\| \"none\" \\| \"required\" \\| {type, function} | No | \"auto\" | Which tool to call |\n| thinking | {type: \"enabled\" \\| \"disabled\"} | No | - | Enable thinking mode |\n| json_mode | boolean | No | false | Enable JSON output mode (supported by both models) |\n| session_id | string | No | - | Session ID for multi-turn conversations |\n\n### Multi-Turn Sessions\n\nUse `session_id` to maintain conversation context across requests:\n\n```json\n// First request - creates session\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What is the capital of France?\"}],\n \"session_id\": \"my-session\"\n}\n\n// Second request - previous context is automatically prepended\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What about Germany?\"}],\n \"session_id\": \"my-session\"\n}\n```\n\nSessions are stored in memory. In STDIO transport they live for the lifetime of the server process. In HTTP transport each MCP session gets its own isolated SessionStore, so session_id values are scoped to the MCP session that created them and are not visible to other connected HTTP clients. Sessions expire after SESSION_TTL_MINUTES (default: 30). Omit session_id for stateless single-turn requests.\n\n### Thinking Mode\n\nThere are two ways to enable thinking mode:\n\n1. Select deepseek-reasoner (automatically routed as chat + thinking):\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Analyze quicksort complexity\"}],\n \"model\": \"deepseek-reasoner\"\n}\n```\n\n2. Explicitly enable thinking on deepseek-chat:\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Analyze quicksort complexity\"}],\n \"model\": \"deepseek-chat\",\n \"thinking\": {\"type\": \"enabled\"}\n}\n```\n\nBoth produce identical results. When thinking is active, temperature/top_p/frequency_penalty/presence_penalty are automatically ignored. The response includes a reasoning_content field with the chain-of-thought.\n\n### JSON Output Mode\n\nGet structured JSON responses:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Return a json object with user data\"}],\n \"model\": \"deepseek-chat\",\n \"json_mode\": true\n}\n```\n\nInclude the word \"json\" in your prompt for best results. Supported by both models.\n\n### Tool Definition Format\n\n```json\n{\n \"type\": \"function\",\n \"function\": {\n \"name\": \"function_name\",\n \"description\": \"What the function does\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"param1\": { \"type\": \"string\", \"description\": \"...\" }\n },\n \"required\": [\"param1\"]\n }\n }\n}\n```\n\n### Response Format\n\n```json\n{\n \"content\": \"Response text\",\n \"reasoning_content\": \"Chain-of-thought (reasoner only)\",\n \"model\": \"deepseek-chat\",\n \"usage\": {\n \"prompt_tokens\": 10,\n \"completion_tokens\": 20,\n \"total_tokens\": 30,\n \"prompt_cache_hit_tokens\": 8,\n \"prompt_cache_miss_tokens\": 2\n },\n \"finish_reason\": \"stop\",\n \"tool_calls\": [\n {\n \"id\": \"call_abc123\",\n \"type\": \"function\",\n \"function\": { \"name\": \"fn_name\", \"arguments\": \"{\\\"key\\\":\\\"value\\\"}\" }\n }\n ],\n \"cost_usd\": 0.000012,\n \"session_id\": \"my-session\"\n}\n```\n\n## Tool: deepseek_sessions\n\nManage conversation sessions:\n\n```json\n{\"action\": \"list\"} // List all active sessions\n{\"action\": \"delete\", \"session_id\": \"my-session\"} // Delete specific session\n{\"action\": \"clear\"} // Clear all sessions\n```\n\n## MCP Resources\n\nThree read-only resources:\n\n| URI | Description |\n|-----|-------------|\n| deepseek://models | Model list with capabilities, context limits, pricing |\n| deepseek://config | Current server config (API key masked: sk-****1234) |\n| deepseek://usage | Real-time usage stats (requests, tokens, costs, sessions, cache ratio) |\n\n## Model Fallback & Circuit Breaker\n\n**Fallback**: On retryable errors (429, 503, timeout), automatically tries the other model:\n- deepseek-chat fails → tries deepseek-reasoner\n- deepseek-reasoner fails → tries deepseek-chat\n- Response includes fallback info when fallback was used\n\n**Circuit Breaker**: Protects against cascading failures:\n- CLOSED → normal operation\n- After CIRCUIT_BREAKER_THRESHOLD failures (default 5) → OPEN (fast-fail for CIRCUIT_BREAKER_RESET_TIMEOUT ms)\n- After timeout → HALF_OPEN (probe with 1 request)\n- Probe succeeds → CLOSED; Probe fails → OPEN again\n\nDisable with FALLBACK_ENABLED=false.\n\n## Function Calling Flow\n\n1. Send messages with `tools` array defining available functions\n2. Model responds with `tool_calls` containing function name and arguments\n3. Execute the function locally\n4. Send result back as a `tool` role message with `tool_call_id`\n5. Model generates final response using the tool result\n\n## Cost Tracking\n\nV3.2 provides cache-aware pricing. Cost display shows:\n- Total cost in USD\n- Cache hit ratio (percentage of prompt tokens served from cache)\n- Estimated savings from cache hits\n\nExample output: `$0.0042 (cache hit: 80%, saved ~$0.0168)`\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-chat | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n| CIRCUIT_BREAKER_THRESHOLD | 5 | Consecutive failures before circuit opens |\n| CIRCUIT_BREAKER_RESET_TIMEOUT | 30000 | Milliseconds before circuit half-opens |\n| MAX_SESSION_MESSAGES | 200 | Max messages per session (sliding window) |\n\n## Prompt Templates (12 total)\n\n### Core Reasoning\n- debug_with_reasoning(code, error?, language?)\n- code_review_deep(code, language?, focus: security|performance|quality|all)\n- research_synthesis(topic, context?, depth: brief|moderate|comprehensive)\n- strategic_planning(goal, context?, constraints?)\n- explain_like_im_five(topic, audience: child|beginner|intermediate)\n\n### Advanced\n- mathematical_proof(statement, context?)\n- argument_validation(argument, type: informal|formal|both)\n- creative_ideation(challenge, constraints?, quantity: 1-20)\n- cost_comparison(task, estimated_tokens)\n- pair_programming(task, language, style: beginner|intermediate|expert)\n\n### Function Calling\n- function_call_debug(tools_json, messages_json, error?)\n- create_function_schema(description, examples?)\n\n## Remote Endpoint (Hosted)\n\nA hosted BYOK endpoint is available at: https://deepseek-mcp.tahirl.com/mcp\n\nSend your DeepSeek API key as `Authorization: Bearer `. No server-side key stored. Powered by Cloudflare Workers (global edge, zero cold start, free tier).\n\nEndpoints:\n- `GET /health` — health check (status, version, transport, timestamp)\n- `GET /` — server info (name, version, description, endpoints)\n- `POST /mcp` — MCP JSON-RPC requests (requires Bearer auth)\n\n## HTTP Transport (Self-Hosted)\n\nRun your own HTTP server instead of stdio:\n\n```bash\nTRANSPORT=http HTTP_PORT=3000 DEEPSEEK_API_KEY=your-key node dist/index.js\n```\n\nEndpoints:\n- `GET /health` — health check (status, version, uptime)\n- `POST /mcp` — MCP JSON-RPC requests (Streamable HTTP protocol)\n- `GET /mcp` — SSE stream (requires Mcp-Session-Id header)\n- `DELETE /mcp` — terminate session\n\nEach MCP session gets its own McpServer instance AND its own SessionStore instance (session-store isolation added in 1.7.0 to prevent cross-session data exposure). DeepSeekClient is shared across sessions since it is a stateless API client.\n\n## Docker\n\n```bash\ndocker build -t deepseek-mcp-server .\ndocker run -d -p 3000:3000 -e DEEPSEEK_API_KEY=your-key deepseek-mcp-server\n```\n\nDocker defaults to HTTP transport. Health check is built in (wget to /health every 30s).\n\n## Architecture\n\n```\nsrc/\n index.ts # Entry point, bootstrap\n server.ts # McpServer factory (version from package.json)\n deepseek-client.ts # DeepSeek API wrapper (circuit breaker + fallback)\n config.ts # Zod-validated config from env vars\n cost.ts # V3.2 cache-aware cost calculation\n schemas.ts # Zod input validation schemas\n types.ts # TypeScript types + type guards\n errors.ts # Custom error classes\n session.ts # In-memory session store\n circuit-breaker.ts # Circuit breaker pattern\n usage-tracker.ts # Usage statistics tracker\n transport-http.ts # Streamable HTTP transport (Express)\n tools/\n deepseek-chat.ts # deepseek_chat tool (sessions + fallback)\n deepseek-sessions.ts # deepseek_sessions tool\n index.ts # Tool registration aggregator\n resources/\n models.ts # deepseek://models resource\n config.ts # deepseek://config resource\n usage.ts # deepseek://usage resource\n index.ts # Resource registration aggregator\n prompts/\n core.ts # 5 core reasoning prompts\n advanced.ts # 5 advanced prompts\n function-calling.ts # 2 function calling prompts\n index.ts # Prompt registration aggregator\n```\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-chat | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n| CIRCUIT_BREAKER_THRESHOLD | 5 | Consecutive failures before circuit opens |\n| CIRCUIT_BREAKER_RESET_TIMEOUT | 30000 | Milliseconds before circuit half-opens |\n| MAX_SESSION_MESSAGES | 200 | Max messages per session (sliding window) |\n| ENABLE_MULTIMODAL | false | Enable multimodal (image) input support |\n| TRANSPORT | stdio | Transport mode: stdio or http |\n| HTTP_PORT | 3000 | HTTP server port (when TRANSPORT=http) |\n\n## Tech Stack\n\n- TypeScript 5.7 (strict mode)\n- @modelcontextprotocol/sdk for MCP protocol\n- OpenAI SDK v6 for API compatibility\n- Zod v4 for validation\n- Vitest for testing (265 tests, ~89% line coverage)\n- Node.js 18+\n\n## Links\n\n- npm: https://www.npmjs.com/package/@arikusi/deepseek-mcp-server\n- GitHub: https://github.com/arikusi/deepseek-mcp-server\n- DeepSeek API: https://api-docs.deepseek.com\n- MCP Spec: https://modelcontextprotocol.io\n"}, "2ca34c8f8e794b091a51ed1c300ea307c6087f79": {"t": 1785749505.2980142, "n": 14385, "h": "cfd6339f1fc96740", "c": "# DeepSeek MCP Server - Complete Documentation\n\nA Model Context Protocol (MCP) server that integrates DeepSeek AI models with MCP-compatible clients like Claude Code and Gemini CLI. Hosted remote endpoint at deepseek-mcp.tahirl.com/mcp with BYOK (Bring Your Own Key) authentication. Also supports local stdio and self-hosted Streamable HTTP transports with Docker deployment. Provides multi-turn sessions, model fallback with circuit breaker, MCP Resources, chat completion, thinking mode, JSON output, function calling, multimodal content support, model-aware cost tracking, and 12 prompt templates.\n\n## Installation\n\n### Remote (No Install)\n\n```bash\n# Claude Code — connect to hosted endpoint with your own API key\nclaude mcp add --transport http deepseek \\\n https://deepseek-mcp.tahirl.com/mcp \\\n --header \"Authorization: Bearer YOUR_DEEPSEEK_API_KEY\"\n```\n\n```json\n// Cursor / Windsurf / VS Code\n{\n \"mcpServers\": {\n \"deepseek\": {\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\",\n \"headers\": { \"Authorization\": \"Bearer ${DEEPSEEK_API_KEY}\" }\n }\n }\n}\n```\n\n### Local (stdio)\n\n```bash\n# Claude Code (all projects)\nclaude mcp add -s user deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Gemini CLI\ngemini mcp add deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Global npm install\nnpm install -g @arikusi/deepseek-mcp-server\n\n# Docker\ndocker run -d -p 3000:3000 -e DEEPSEEK_API_KEY=your-key ghcr.io/arikusi/deepseek-mcp-server\n```\n\nGet API key: https://platform.deepseek.com\n\n## Models\n\nBoth models run DeepSeek-V3.2 with unified pricing. They are the same underlying model with different modes:\n- deepseek-chat = V3.2 non-thinking mode\n- deepseek-reasoner = V3.2 thinking mode (transparently routed as deepseek-chat + thinking:{type:\"enabled\"})\n\n### deepseek-chat\n- General conversations, coding, content generation\n- Non-thinking mode by default. Can enable thinking via `thinking: {type: \"enabled\"}` parameter\n- 128K context, 8K max output (default 4K), 64K max output when thinking enabled\n- Supports thinking mode, JSON mode, function calling, FIM completion\n- Pricing: $0.028/1M cache hit, $0.28/1M cache miss, $0.42/1M output\n\n### deepseek-reasoner\n- Complex reasoning, math, logic, multi-step tasks\n- Transparently routed as deepseek-chat with thinking:{type:\"enabled\"}\n- This enables full feature parity including function calling, which the raw deepseek-reasoner API does not support\n- 128K context, 64K max output (default 32K)\n- Supports JSON mode, function calling (via transparent routing)\n- Pricing: $0.028/1M cache hit, $0.28/1M cache miss, $0.42/1M output\n\n### Model Routing Table\n\n| User selects | Sent to API | Thinking | reasoning_content | Function calling |\n|---|---|---|---|---|\n| deepseek-chat | deepseek-chat | Off | No | Yes |\n| deepseek-chat + thinking:enabled | deepseek-chat + thinking | On | Yes | Yes |\n| deepseek-reasoner | deepseek-chat + thinking | On | Yes | Yes |\n\n## Tool: deepseek_chat\n\n### Input Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| messages | Array<{role, content, tool_call_id?}> | Yes | - | Conversation messages. Roles: system, user, assistant, tool. Content can be string or array of content parts (text/image_url) when multimodal is enabled |\n| model | \"deepseek-chat\" \\| \"deepseek-reasoner\" | No | \"deepseek-chat\" | Model to use |\n| temperature | number (0-2) | No | 1.0 | Sampling temperature. Ignored when thinking enabled |\n| max_tokens | number (1-65536) | No | model max | Max tokens: chat=8192, reasoner=65536. Both have 128K context |\n| stream | boolean | No | false | Enable streaming mode |\n| tools | Array (max 128) | No | - | Function calling tool definitions |\n| tool_choice | \"auto\" \\| \"none\" \\| \"required\" \\| {type, function} | No | \"auto\" | Which tool to call |\n| thinking | {type: \"enabled\" \\| \"disabled\"} | No | - | Enable thinking mode |\n| json_mode | boolean | No | false | Enable JSON output mode (supported by both models) |\n| session_id | string | No | - | Session ID for multi-turn conversations |\n\n### Multi-Turn Sessions\n\nUse `session_id` to maintain conversation context across requests:\n\n```json\n// First request - creates session\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What is the capital of France?\"}],\n \"session_id\": \"my-session\"\n}\n\n// Second request - previous context is automatically prepended\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What about Germany?\"}],\n \"session_id\": \"my-session\"\n}\n```\n\nSessions are stored in memory for the lifetime of the MCP server process. They expire after SESSION_TTL_MINUTES (default: 30). Omit session_id for stateless single-turn requests.\n\n### Thinking Mode\n\nThere are two ways to enable thinking mode:\n\n1. Select deepseek-reasoner (automatically routed as chat + thinking):\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Analyze quicksort complexity\"}],\n \"model\": \"deepseek-reasoner\"\n}\n```\n\n2. Explicitly enable thinking on deepseek-chat:\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Analyze quicksort complexity\"}],\n \"model\": \"deepseek-chat\",\n \"thinking\": {\"type\": \"enabled\"}\n}\n```\n\nBoth produce identical results. When thinking is active, temperature/top_p/frequency_penalty/presence_penalty are automatically ignored. The response includes a reasoning_content field with the chain-of-thought.\n\n### JSON Output Mode\n\nGet structured JSON responses:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Return a json object with user data\"}],\n \"model\": \"deepseek-chat\",\n \"json_mode\": true\n}\n```\n\nInclude the word \"json\" in your prompt for best results. Supported by both models.\n\n### Tool Definition Format\n\n```json\n{\n \"type\": \"function\",\n \"function\": {\n \"name\": \"function_name\",\n \"description\": \"What the function does\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"param1\": { \"type\": \"string\", \"description\": \"...\" }\n },\n \"required\": [\"param1\"]\n }\n }\n}\n```\n\n### Response Format\n\n```json\n{\n \"content\": \"Response text\",\n \"reasoning_content\": \"Chain-of-thought (reasoner only)\",\n \"model\": \"deepseek-chat\",\n \"usage\": {\n \"prompt_tokens\": 10,\n \"completion_tokens\": 20,\n \"total_tokens\": 30,\n \"prompt_cache_hit_tokens\": 8,\n \"prompt_cache_miss_tokens\": 2\n },\n \"finish_reason\": \"stop\",\n \"tool_calls\": [\n {\n \"id\": \"call_abc123\",\n \"type\": \"function\",\n \"function\": { \"name\": \"fn_name\", \"arguments\": \"{\\\"key\\\":\\\"value\\\"}\" }\n }\n ],\n \"cost_usd\": 0.000012,\n \"session_id\": \"my-session\"\n}\n```\n\n## Tool: deepseek_sessions\n\nManage conversation sessions:\n\n```json\n{\"action\": \"list\"} // List all active sessions\n{\"action\": \"delete\", \"session_id\": \"my-session\"} // Delete specific session\n{\"action\": \"clear\"} // Clear all sessions\n```\n\n## MCP Resources\n\nThree read-only resources:\n\n| URI | Description |\n|-----|-------------|\n| deepseek://models | Model list with capabilities, context limits, pricing |\n| deepseek://config | Current server config (API key masked: sk-****1234) |\n| deepseek://usage | Real-time usage stats (requests, tokens, costs, sessions, cache ratio) |\n\n## Model Fallback & Circuit Breaker\n\n**Fallback**: On retryable errors (429, 503, timeout), automatically tries the other model:\n- deepseek-chat fails → tries deepseek-reasoner\n- deepseek-reasoner fails → tries deepseek-chat\n- Response includes fallback info when fallback was used\n\n**Circuit Breaker**: Protects against cascading failures:\n- CLOSED → normal operation\n- After CIRCUIT_BREAKER_THRESHOLD failures (default 5) → OPEN (fast-fail for CIRCUIT_BREAKER_RESET_TIMEOUT ms)\n- After timeout → HALF_OPEN (probe with 1 request)\n- Probe succeeds → CLOSED; Probe fails → OPEN again\n\nDisable with FALLBACK_ENABLED=false.\n\n## Function Calling Flow\n\n1. Send messages with `tools` array defining available functions\n2. Model responds with `tool_calls` containing function name and arguments\n3. Execute the function locally\n4. Send result back as a `tool` role message with `tool_call_id`\n5. Model generates final response using the tool result\n\n## Cost Tracking\n\nV3.2 provides cache-aware pricing. Cost display shows:\n- Total cost in USD\n- Cache hit ratio (percentage of prompt tokens served from cache)\n- Estimated savings from cache hits\n\nExample output: `$0.0042 (cache hit: 80%, saved ~$0.0168)`\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-chat | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n| CIRCUIT_BREAKER_THRESHOLD | 5 | Consecutive failures before circuit opens |\n| CIRCUIT_BREAKER_RESET_TIMEOUT | 30000 | Milliseconds before circuit half-opens |\n| MAX_SESSION_MESSAGES | 200 | Max messages per session (sliding window) |\n\n## Prompt Templates (12 total)\n\n### Core Reasoning\n- debug_with_reasoning(code, error?, language?)\n- code_review_deep(code, language?, focus: security|performance|quality|all)\n- research_synthesis(topic, context?, depth: brief|moderate|comprehensive)\n- strategic_planning(goal, context?, constraints?)\n- explain_like_im_five(topic, audience: child|beginner|intermediate)\n\n### Advanced\n- mathematical_proof(statement, context?)\n- argument_validation(argument, type: informal|formal|both)\n- creative_ideation(challenge, constraints?, quantity: 1-20)\n- cost_comparison(task, estimated_tokens)\n- pair_programming(task, language, style: beginner|intermediate|expert)\n\n### Function Calling\n- function_call_debug(tools_json, messages_json, error?)\n- create_function_schema(description, examples?)\n\n## Remote Endpoint (Hosted)\n\nA hosted BYOK endpoint is available at: https://deepseek-mcp.tahirl.com/mcp\n\nSend your DeepSeek API key as `Authorization: Bearer `. No server-side key stored. Powered by Cloudflare Workers (global edge, zero cold start, free tier).\n\nEndpoints:\n- `GET /health` — health check (status, version, transport, timestamp)\n- `GET /` — server info (name, version, description, endpoints)\n- `POST /mcp` — MCP JSON-RPC requests (requires Bearer auth)\n\n## HTTP Transport (Self-Hosted)\n\nRun your own HTTP server instead of stdio:\n\n```bash\nTRANSPORT=http HTTP_PORT=3000 DEEPSEEK_API_KEY=your-key node dist/index.js\n```\n\nEndpoints:\n- `GET /health` — health check (status, version, uptime)\n- `POST /mcp` — MCP JSON-RPC requests (Streamable HTTP protocol)\n- `GET /mcp` — SSE stream (requires Mcp-Session-Id header)\n- `DELETE /mcp` — terminate session\n\nEach MCP session gets its own McpServer instance. DeepSeekClient is shared across sessions.\n\n## Docker\n\n```bash\ndocker build -t deepseek-mcp-server .\ndocker run -d -p 3000:3000 -e DEEPSEEK_API_KEY=your-key deepseek-mcp-server\n```\n\nDocker defaults to HTTP transport. Health check is built in (wget to /health every 30s).\n\n## Architecture\n\n```\nsrc/\n index.ts # Entry point, bootstrap\n server.ts # McpServer factory (version from package.json)\n deepseek-client.ts # DeepSeek API wrapper (circuit breaker + fallback)\n config.ts # Zod-validated config from env vars\n cost.ts # V3.2 cache-aware cost calculation\n schemas.ts # Zod input validation schemas\n types.ts # TypeScript types + type guards\n errors.ts # Custom error classes\n session.ts # In-memory session store\n circuit-breaker.ts # Circuit breaker pattern\n usage-tracker.ts # Usage statistics tracker\n transport-http.ts # Streamable HTTP transport (Express)\n tools/\n deepseek-chat.ts # deepseek_chat tool (sessions + fallback)\n deepseek-sessions.ts # deepseek_sessions tool\n index.ts # Tool registration aggregator\n resources/\n models.ts # deepseek://models resource\n config.ts # deepseek://config resource\n usage.ts # deepseek://usage resource\n index.ts # Resource registration aggregator\n prompts/\n core.ts # 5 core reasoning prompts\n advanced.ts # 5 advanced prompts\n function-calling.ts # 2 function calling prompts\n index.ts # Prompt registration aggregator\n```\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-chat | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n| CIRCUIT_BREAKER_THRESHOLD | 5 | Consecutive failures before circuit opens |\n| CIRCUIT_BREAKER_RESET_TIMEOUT | 30000 | Milliseconds before circuit half-opens |\n| MAX_SESSION_MESSAGES | 200 | Max messages per session (sliding window) |\n| ENABLE_MULTIMODAL | false | Enable multimodal (image) input support |\n| TRANSPORT | stdio | Transport mode: stdio or http |\n| HTTP_PORT | 3000 | HTTP server port (when TRANSPORT=http) |\n\n## Tech Stack\n\n- TypeScript 5.7 (strict mode)\n- @modelcontextprotocol/sdk for MCP protocol\n- OpenAI SDK v6 for API compatibility\n- Zod v4 for validation\n- Vitest for testing (253 tests)\n- Node.js 18+\n\n## Links\n\n- npm: https://www.npmjs.com/package/@arikusi/deepseek-mcp-server\n- GitHub: https://github.com/arikusi/deepseek-mcp-server\n- DeepSeek API: https://api-docs.deepseek.com\n- MCP Spec: https://modelcontextprotocol.io\n"}, "03f0206a0a6601690b33d2028fb8feec4e8300a9": {"t": 1785749506.3867774, "n": 13349, "h": "e80f00026e80a002", "c": "# DeepSeek MCP Server - Complete Documentation\n\nA Model Context Protocol (MCP) server that integrates DeepSeek AI models with MCP-compatible clients like Claude Code and Gemini CLI. Hosted remote endpoint at deepseek-mcp.tahirl.com/mcp with BYOK (Bring Your Own Key) authentication. Also supports local stdio and self-hosted Streamable HTTP transports with Docker deployment. Provides multi-turn sessions, model fallback with circuit breaker, MCP Resources, chat completion, thinking mode, JSON output, function calling, multimodal content support, model-aware cost tracking, and 12 prompt templates.\n\n## Installation\n\n### Remote (No Install)\n\n```bash\n# Claude Code — connect to hosted endpoint with your own API key\nclaude mcp add --transport http deepseek \\\n https://deepseek-mcp.tahirl.com/mcp \\\n --header \"Authorization: Bearer YOUR_DEEPSEEK_API_KEY\"\n```\n\n```json\n// Cursor / Windsurf / VS Code\n{\n \"mcpServers\": {\n \"deepseek\": {\n \"url\": \"https://deepseek-mcp.tahirl.com/mcp\",\n \"headers\": { \"Authorization\": \"Bearer ${DEEPSEEK_API_KEY}\" }\n }\n }\n}\n```\n\n### Local (stdio)\n\n```bash\n# Claude Code (all projects)\nclaude mcp add -s user deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Gemini CLI\ngemini mcp add deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Global npm install\nnpm install -g @arikusi/deepseek-mcp-server\n\n# Docker\ndocker run -d -p 3000:3000 -e DEEPSEEK_API_KEY=your-key ghcr.io/arikusi/deepseek-mcp-server\n```\n\nGet API key: https://platform.deepseek.com\n\n## Models\n\nBoth models run DeepSeek-V3.2 with unified pricing.\n\n### deepseek-chat\n- General conversations, coding, content generation\n- Non-thinking mode (can enable thinking via parameter)\n- 128K context, 8K max output (default 4K)\n- Supports thinking mode, JSON mode, function calling, FIM completion\n- Pricing: $0.028/1M cache hit, $0.28/1M cache miss, $0.42/1M output\n\n### deepseek-reasoner\n- Complex reasoning, math, logic, multi-step tasks\n- Thinking mode (always active), shows chain-of-thought in reasoning_content\n- 128K context, 64K max output (default 32K)\n- Supports JSON mode, function calling\n- Pricing: $0.028/1M cache hit, $0.28/1M cache miss, $0.42/1M output\n\n## Tool: deepseek_chat\n\n### Input Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| messages | Array<{role, content, tool_call_id?}> | Yes | - | Conversation messages. Roles: system, user, assistant, tool. Content can be string or array of content parts (text/image_url) when multimodal is enabled |\n| model | \"deepseek-chat\" \\| \"deepseek-reasoner\" | No | \"deepseek-chat\" | Model to use |\n| temperature | number (0-2) | No | 1.0 | Sampling temperature. Ignored when thinking enabled |\n| max_tokens | number (1-65536) | No | model max | Max tokens: chat=8192, reasoner=65536. Both have 128K context |\n| stream | boolean | No | false | Enable streaming mode |\n| tools | Array (max 128) | No | - | Function calling tool definitions |\n| tool_choice | \"auto\" \\| \"none\" \\| \"required\" \\| {type, function} | No | \"auto\" | Which tool to call |\n| thinking | {type: \"enabled\" \\| \"disabled\"} | No | - | Enable thinking mode |\n| json_mode | boolean | No | false | Enable JSON output mode (supported by both models) |\n| session_id | string | No | - | Session ID for multi-turn conversations |\n\n### Multi-Turn Sessions\n\nUse `session_id` to maintain conversation context across requests:\n\n```json\n// First request - creates session\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What is the capital of France?\"}],\n \"session_id\": \"my-session\"\n}\n\n// Second request - previous context is automatically prepended\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What about Germany?\"}],\n \"session_id\": \"my-session\"\n}\n```\n\nSessions are stored in memory for the lifetime of the MCP server process. They expire after SESSION_TTL_MINUTES (default: 30). Omit session_id for stateless single-turn requests.\n\n### Thinking Mode\n\nEnable thinking mode on deepseek-chat:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Analyze quicksort complexity\"}],\n \"model\": \"deepseek-chat\",\n \"thinking\": {\"type\": \"enabled\"}\n}\n```\n\nWhen enabled, temperature/top_p/frequency_penalty/presence_penalty are automatically ignored with a logged warning. deepseek-reasoner has implicit thinking (always active).\n\n### JSON Output Mode\n\nGet structured JSON responses:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Return a json object with user data\"}],\n \"model\": \"deepseek-chat\",\n \"json_mode\": true\n}\n```\n\nInclude the word \"json\" in your prompt for best results. Supported by both models.\n\n### Tool Definition Format\n\n```json\n{\n \"type\": \"function\",\n \"function\": {\n \"name\": \"function_name\",\n \"description\": \"What the function does\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"param1\": { \"type\": \"string\", \"description\": \"...\" }\n },\n \"required\": [\"param1\"]\n }\n }\n}\n```\n\n### Response Format\n\n```json\n{\n \"content\": \"Response text\",\n \"reasoning_content\": \"Chain-of-thought (reasoner only)\",\n \"model\": \"deepseek-chat\",\n \"usage\": {\n \"prompt_tokens\": 10,\n \"completion_tokens\": 20,\n \"total_tokens\": 30,\n \"prompt_cache_hit_tokens\": 8,\n \"prompt_cache_miss_tokens\": 2\n },\n \"finish_reason\": \"stop\",\n \"tool_calls\": [\n {\n \"id\": \"call_abc123\",\n \"type\": \"function\",\n \"function\": { \"name\": \"fn_name\", \"arguments\": \"{\\\"key\\\":\\\"value\\\"}\" }\n }\n ],\n \"cost_usd\": 0.000012,\n \"session_id\": \"my-session\"\n}\n```\n\n## Tool: deepseek_sessions\n\nManage conversation sessions:\n\n```json\n{\"action\": \"list\"} // List all active sessions\n{\"action\": \"delete\", \"session_id\": \"my-session\"} // Delete specific session\n{\"action\": \"clear\"} // Clear all sessions\n```\n\n## MCP Resources\n\nThree read-only resources:\n\n| URI | Description |\n|-----|-------------|\n| deepseek://models | Model list with capabilities, context limits, pricing |\n| deepseek://config | Current server config (API key masked: sk-****1234) |\n| deepseek://usage | Real-time usage stats (requests, tokens, costs, sessions, cache ratio) |\n\n## Model Fallback & Circuit Breaker\n\n**Fallback**: On retryable errors (429, 503, timeout), automatically tries the other model:\n- deepseek-chat fails → tries deepseek-reasoner\n- deepseek-reasoner fails → tries deepseek-chat\n- Response includes fallback info when fallback was used\n\n**Circuit Breaker**: Protects against cascading failures:\n- CLOSED → normal operation\n- After CIRCUIT_BREAKER_THRESHOLD failures (default 5) → OPEN (fast-fail for CIRCUIT_BREAKER_RESET_TIMEOUT ms)\n- After timeout → HALF_OPEN (probe with 1 request)\n- Probe succeeds → CLOSED; Probe fails → OPEN again\n\nDisable with FALLBACK_ENABLED=false.\n\n## Function Calling Flow\n\n1. Send messages with `tools` array defining available functions\n2. Model responds with `tool_calls` containing function name and arguments\n3. Execute the function locally\n4. Send result back as a `tool` role message with `tool_call_id`\n5. Model generates final response using the tool result\n\n## Cost Tracking\n\nV3.2 provides cache-aware pricing. Cost display shows:\n- Total cost in USD\n- Cache hit ratio (percentage of prompt tokens served from cache)\n- Estimated savings from cache hits\n\nExample output: `$0.0042 (cache hit: 80%, saved ~$0.0168)`\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-chat | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n| CIRCUIT_BREAKER_THRESHOLD | 5 | Consecutive failures before circuit opens |\n| CIRCUIT_BREAKER_RESET_TIMEOUT | 30000 | Milliseconds before circuit half-opens |\n| MAX_SESSION_MESSAGES | 200 | Max messages per session (sliding window) |\n\n## Prompt Templates (12 total)\n\n### Core Reasoning\n- debug_with_reasoning(code, error?, language?)\n- code_review_deep(code, language?, focus: security|performance|quality|all)\n- research_synthesis(topic, context?, depth: brief|moderate|comprehensive)\n- strategic_planning(goal, context?, constraints?)\n- explain_like_im_five(topic, audience: child|beginner|intermediate)\n\n### Advanced\n- mathematical_proof(statement, context?)\n- argument_validation(argument, type: informal|formal|both)\n- creative_ideation(challenge, constraints?, quantity: 1-20)\n- cost_comparison(task, estimated_tokens)\n- pair_programming(task, language, style: beginner|intermediate|expert)\n\n### Function Calling\n- function_call_debug(tools_json, messages_json, error?)\n- create_function_schema(description, examples?)\n\n## Remote Endpoint (Hosted)\n\nA hosted BYOK endpoint is available at: https://deepseek-mcp.tahirl.com/mcp\n\nSend your DeepSeek API key as `Authorization: Bearer `. No server-side key stored. Powered by Cloudflare Workers (global edge, zero cold start, free tier).\n\nEndpoints:\n- `GET /health` — health check (status, version, transport, timestamp)\n- `GET /` — server info (name, version, description, endpoints)\n- `POST /mcp` — MCP JSON-RPC requests (requires Bearer auth)\n\n## HTTP Transport (Self-Hosted)\n\nRun your own HTTP server instead of stdio:\n\n```bash\nTRANSPORT=http HTTP_PORT=3000 DEEPSEEK_API_KEY=your-key node dist/index.js\n```\n\nEndpoints:\n- `GET /health` — health check (status, version, uptime)\n- `POST /mcp` — MCP JSON-RPC requests (Streamable HTTP protocol)\n- `GET /mcp` — SSE stream (requires Mcp-Session-Id header)\n- `DELETE /mcp` — terminate session\n\nEach MCP session gets its own McpServer instance. DeepSeekClient is shared across sessions.\n\n## Docker\n\n```bash\ndocker build -t deepseek-mcp-server .\ndocker run -d -p 3000:3000 -e DEEPSEEK_API_KEY=your-key deepseek-mcp-server\n```\n\nDocker defaults to HTTP transport. Health check is built in (wget to /health every 30s).\n\n## Architecture\n\n```\nsrc/\n index.ts # Entry point, bootstrap\n server.ts # McpServer factory (version from package.json)\n deepseek-client.ts # DeepSeek API wrapper (circuit breaker + fallback)\n config.ts # Zod-validated config from env vars\n cost.ts # V3.2 cache-aware cost calculation\n schemas.ts # Zod input validation schemas\n types.ts # TypeScript types + type guards\n errors.ts # Custom error classes\n session.ts # In-memory session store\n circuit-breaker.ts # Circuit breaker pattern\n usage-tracker.ts # Usage statistics tracker\n transport-http.ts # Streamable HTTP transport (Express)\n tools/\n deepseek-chat.ts # deepseek_chat tool (sessions + fallback)\n deepseek-sessions.ts # deepseek_sessions tool\n index.ts # Tool registration aggregator\n resources/\n models.ts # deepseek://models resource\n config.ts # deepseek://config resource\n usage.ts # deepseek://usage resource\n index.ts # Resource registration aggregator\n prompts/\n core.ts # 5 core reasoning prompts\n advanced.ts # 5 advanced prompts\n function-calling.ts # 2 function calling prompts\n index.ts # Prompt registration aggregator\n```\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-chat | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n| CIRCUIT_BREAKER_THRESHOLD | 5 | Consecutive failures before circuit opens |\n| CIRCUIT_BREAKER_RESET_TIMEOUT | 30000 | Milliseconds before circuit half-opens |\n| MAX_SESSION_MESSAGES | 200 | Max messages per session (sliding window) |\n| ENABLE_MULTIMODAL | false | Enable multimodal (image) input support |\n| TRANSPORT | stdio | Transport mode: stdio or http |\n| HTTP_PORT | 3000 | HTTP server port (when TRANSPORT=http) |\n\n## Tech Stack\n\n- TypeScript 5.7 (strict mode)\n- @modelcontextprotocol/sdk for MCP protocol\n- OpenAI SDK v6 for API compatibility\n- Zod v4 for validation\n- Vitest for testing (253 tests)\n- Node.js 18+\n\n## Links\n\n- npm: https://www.npmjs.com/package/@arikusi/deepseek-mcp-server\n- GitHub: https://github.com/arikusi/deepseek-mcp-server\n- DeepSeek API: https://api-docs.deepseek.com\n- MCP Spec: https://modelcontextprotocol.io\n"}, "9e364641ed058d311518ae2261fc5c5d95e34c53": {"t": 1785749508.5018976, "n": 12263, "h": "ea4aa4131b14ec76", "c": "# DeepSeek MCP Server - Complete Documentation\n\nA Model Context Protocol (MCP) server that integrates DeepSeek AI models with MCP-compatible clients like Claude Code and Gemini CLI. Supports stdio and Streamable HTTP transports with Docker deployment. Provides multi-turn sessions, model fallback with circuit breaker, MCP Resources, chat completion, thinking mode, JSON output, function calling, multimodal content support, model-aware cost tracking, and 12 prompt templates.\n\n## Installation\n\n```bash\n# Claude Code (all projects)\nclaude mcp add -s user deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Gemini CLI\ngemini mcp add deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Global npm install\nnpm install -g @arikusi/deepseek-mcp-server\n\n# Docker\ndocker run -d -p 3000:3000 -e DEEPSEEK_API_KEY=your-key ghcr.io/arikusi/deepseek-mcp-server\n```\n\nGet API key: https://platform.deepseek.com\n\n## Models\n\nBoth models run DeepSeek-V3.2 with unified pricing.\n\n### deepseek-chat\n- General conversations, coding, content generation\n- Non-thinking mode (can enable thinking via parameter)\n- 128K context, 8K max output (default 4K)\n- Supports thinking mode, JSON mode, function calling, FIM completion\n- Pricing: $0.028/1M cache hit, $0.28/1M cache miss, $0.42/1M output\n\n### deepseek-reasoner\n- Complex reasoning, math, logic, multi-step tasks\n- Thinking mode (always active), shows chain-of-thought in reasoning_content\n- 128K context, 64K max output (default 32K)\n- Supports JSON mode, function calling\n- Pricing: $0.028/1M cache hit, $0.28/1M cache miss, $0.42/1M output\n\n## Tool: deepseek_chat\n\n### Input Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| messages | Array<{role, content, tool_call_id?}> | Yes | - | Conversation messages. Roles: system, user, assistant, tool. Content can be string or array of content parts (text/image_url) when multimodal is enabled |\n| model | \"deepseek-chat\" \\| \"deepseek-reasoner\" | No | \"deepseek-chat\" | Model to use |\n| temperature | number (0-2) | No | 1.0 | Sampling temperature. Ignored when thinking enabled |\n| max_tokens | number (1-65536) | No | model max | Max tokens: chat=8192, reasoner=65536. Both have 128K context |\n| stream | boolean | No | false | Enable streaming mode |\n| tools | Array (max 128) | No | - | Function calling tool definitions |\n| tool_choice | \"auto\" \\| \"none\" \\| \"required\" \\| {type, function} | No | \"auto\" | Which tool to call |\n| thinking | {type: \"enabled\" \\| \"disabled\"} | No | - | Enable thinking mode |\n| json_mode | boolean | No | false | Enable JSON output mode (supported by both models) |\n| session_id | string | No | - | Session ID for multi-turn conversations |\n\n### Multi-Turn Sessions\n\nUse `session_id` to maintain conversation context across requests:\n\n```json\n// First request - creates session\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What is the capital of France?\"}],\n \"session_id\": \"my-session\"\n}\n\n// Second request - previous context is automatically prepended\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What about Germany?\"}],\n \"session_id\": \"my-session\"\n}\n```\n\nSessions are stored in memory for the lifetime of the MCP server process. They expire after SESSION_TTL_MINUTES (default: 30). Omit session_id for stateless single-turn requests.\n\n### Thinking Mode\n\nEnable thinking mode on deepseek-chat:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Analyze quicksort complexity\"}],\n \"model\": \"deepseek-chat\",\n \"thinking\": {\"type\": \"enabled\"}\n}\n```\n\nWhen enabled, temperature/top_p/frequency_penalty/presence_penalty are automatically ignored with a logged warning. deepseek-reasoner has implicit thinking (always active).\n\n### JSON Output Mode\n\nGet structured JSON responses:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Return a json object with user data\"}],\n \"model\": \"deepseek-chat\",\n \"json_mode\": true\n}\n```\n\nInclude the word \"json\" in your prompt for best results. Supported by both models.\n\n### Tool Definition Format\n\n```json\n{\n \"type\": \"function\",\n \"function\": {\n \"name\": \"function_name\",\n \"description\": \"What the function does\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"param1\": { \"type\": \"string\", \"description\": \"...\" }\n },\n \"required\": [\"param1\"]\n }\n }\n}\n```\n\n### Response Format\n\n```json\n{\n \"content\": \"Response text\",\n \"reasoning_content\": \"Chain-of-thought (reasoner only)\",\n \"model\": \"deepseek-chat\",\n \"usage\": {\n \"prompt_tokens\": 10,\n \"completion_tokens\": 20,\n \"total_tokens\": 30,\n \"prompt_cache_hit_tokens\": 8,\n \"prompt_cache_miss_tokens\": 2\n },\n \"finish_reason\": \"stop\",\n \"tool_calls\": [\n {\n \"id\": \"call_abc123\",\n \"type\": \"function\",\n \"function\": { \"name\": \"fn_name\", \"arguments\": \"{\\\"key\\\":\\\"value\\\"}\" }\n }\n ],\n \"cost_usd\": 0.000012,\n \"session_id\": \"my-session\"\n}\n```\n\n## Tool: deepseek_sessions\n\nManage conversation sessions:\n\n```json\n{\"action\": \"list\"} // List all active sessions\n{\"action\": \"delete\", \"session_id\": \"my-session\"} // Delete specific session\n{\"action\": \"clear\"} // Clear all sessions\n```\n\n## MCP Resources\n\nThree read-only resources:\n\n| URI | Description |\n|-----|-------------|\n| deepseek://models | Model list with capabilities, context limits, pricing |\n| deepseek://config | Current server config (API key masked: sk-****1234) |\n| deepseek://usage | Real-time usage stats (requests, tokens, costs, sessions, cache ratio) |\n\n## Model Fallback & Circuit Breaker\n\n**Fallback**: On retryable errors (429, 503, timeout), automatically tries the other model:\n- deepseek-chat fails → tries deepseek-reasoner\n- deepseek-reasoner fails → tries deepseek-chat\n- Response includes fallback info when fallback was used\n\n**Circuit Breaker**: Protects against cascading failures:\n- CLOSED → normal operation\n- After CIRCUIT_BREAKER_THRESHOLD failures (default 5) → OPEN (fast-fail for CIRCUIT_BREAKER_RESET_TIMEOUT ms)\n- After timeout → HALF_OPEN (probe with 1 request)\n- Probe succeeds → CLOSED; Probe fails → OPEN again\n\nDisable with FALLBACK_ENABLED=false.\n\n## Function Calling Flow\n\n1. Send messages with `tools` array defining available functions\n2. Model responds with `tool_calls` containing function name and arguments\n3. Execute the function locally\n4. Send result back as a `tool` role message with `tool_call_id`\n5. Model generates final response using the tool result\n\n## Cost Tracking\n\nV3.2 provides cache-aware pricing. Cost display shows:\n- Total cost in USD\n- Cache hit ratio (percentage of prompt tokens served from cache)\n- Estimated savings from cache hits\n\nExample output: `$0.0042 (cache hit: 80%, saved ~$0.0168)`\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-chat | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n| CIRCUIT_BREAKER_THRESHOLD | 5 | Consecutive failures before circuit opens |\n| CIRCUIT_BREAKER_RESET_TIMEOUT | 30000 | Milliseconds before circuit half-opens |\n| MAX_SESSION_MESSAGES | 200 | Max messages per session (sliding window) |\n\n## Prompt Templates (12 total)\n\n### Core Reasoning\n- debug_with_reasoning(code, error?, language?)\n- code_review_deep(code, language?, focus: security|performance|quality|all)\n- research_synthesis(topic, context?, depth: brief|moderate|comprehensive)\n- strategic_planning(goal, context?, constraints?)\n- explain_like_im_five(topic, audience: child|beginner|intermediate)\n\n### Advanced\n- mathematical_proof(statement, context?)\n- argument_validation(argument, type: informal|formal|both)\n- creative_ideation(challenge, constraints?, quantity: 1-20)\n- cost_comparison(task, estimated_tokens)\n- pair_programming(task, language, style: beginner|intermediate|expert)\n\n### Function Calling\n- function_call_debug(tools_json, messages_json, error?)\n- create_function_schema(description, examples?)\n\n## HTTP Transport\n\nRun as a remote HTTP server instead of stdio:\n\n```bash\nTRANSPORT=http HTTP_PORT=3000 DEEPSEEK_API_KEY=your-key node dist/index.js\n```\n\nEndpoints:\n- `GET /health` — health check (status, version, uptime)\n- `POST /mcp` — MCP JSON-RPC requests (Streamable HTTP protocol)\n- `GET /mcp` — SSE stream (requires Mcp-Session-Id header)\n- `DELETE /mcp` — terminate session\n\nEach MCP session gets its own McpServer instance. DeepSeekClient is shared across sessions.\n\n## Docker\n\n```bash\ndocker build -t deepseek-mcp-server .\ndocker run -d -p 3000:3000 -e DEEPSEEK_API_KEY=your-key deepseek-mcp-server\n```\n\nDocker defaults to HTTP transport. Health check is built in (wget to /health every 30s).\n\n## Architecture\n\n```\nsrc/\n index.ts # Entry point, bootstrap\n server.ts # McpServer factory (version from package.json)\n deepseek-client.ts # DeepSeek API wrapper (circuit breaker + fallback)\n config.ts # Zod-validated config from env vars\n cost.ts # V3.2 cache-aware cost calculation\n schemas.ts # Zod input validation schemas\n types.ts # TypeScript types + type guards\n errors.ts # Custom error classes\n session.ts # In-memory session store\n circuit-breaker.ts # Circuit breaker pattern\n usage-tracker.ts # Usage statistics tracker\n transport-http.ts # Streamable HTTP transport (Express)\n tools/\n deepseek-chat.ts # deepseek_chat tool (sessions + fallback)\n deepseek-sessions.ts # deepseek_sessions tool\n index.ts # Tool registration aggregator\n resources/\n models.ts # deepseek://models resource\n config.ts # deepseek://config resource\n usage.ts # deepseek://usage resource\n index.ts # Resource registration aggregator\n prompts/\n core.ts # 5 core reasoning prompts\n advanced.ts # 5 advanced prompts\n function-calling.ts # 2 function calling prompts\n index.ts # Prompt registration aggregator\n```\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-chat | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n| CIRCUIT_BREAKER_THRESHOLD | 5 | Consecutive failures before circuit opens |\n| CIRCUIT_BREAKER_RESET_TIMEOUT | 30000 | Milliseconds before circuit half-opens |\n| MAX_SESSION_MESSAGES | 200 | Max messages per session (sliding window) |\n| ENABLE_MULTIMODAL | false | Enable multimodal (image) input support |\n| TRANSPORT | stdio | Transport mode: stdio or http |\n| HTTP_PORT | 3000 | HTTP server port (when TRANSPORT=http) |\n\n## Tech Stack\n\n- TypeScript 5.7 (strict mode)\n- @modelcontextprotocol/sdk for MCP protocol\n- OpenAI SDK v6 for API compatibility\n- Zod v4 for validation\n- Vitest for testing (253 tests)\n- Node.js 18+\n\n## Links\n\n- npm: https://www.npmjs.com/package/@arikusi/deepseek-mcp-server\n- GitHub: https://github.com/arikusi/deepseek-mcp-server\n- DeepSeek API: https://api-docs.deepseek.com\n- MCP Spec: https://modelcontextprotocol.io\n"}, "3f1d3d9e6a498db02e60654dfe70fa5fb629e0a0": {"t": 1785749509.5837052, "n": 11209, "h": "d25556c966724ad7", "c": "# DeepSeek MCP Server - Complete Documentation\n\nA Model Context Protocol (MCP) server that integrates DeepSeek AI models with MCP-compatible clients like Claude Code and Gemini CLI. Provides multi-turn sessions, model fallback with circuit breaker, MCP Resources, chat completion, thinking mode, JSON output, function calling, multimodal content support, model-aware cost tracking, and 12 prompt templates.\n\n## Installation\n\n```bash\n# Claude Code (all projects)\nclaude mcp add -s user deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Gemini CLI\ngemini mcp add deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Global npm install\nnpm install -g @arikusi/deepseek-mcp-server\n```\n\nGet API key: https://platform.deepseek.com\n\n## Models\n\nBoth models run DeepSeek-V3.2 with unified pricing.\n\n### deepseek-chat\n- General conversations, coding, content generation\n- Non-thinking mode (can enable thinking via parameter)\n- 128K context, 8K max output (default 4K)\n- Supports thinking mode, JSON mode, function calling, FIM completion\n- Pricing: $0.028/1M cache hit, $0.28/1M cache miss, $0.42/1M output\n\n### deepseek-reasoner\n- Complex reasoning, math, logic, multi-step tasks\n- Thinking mode (always active), shows chain-of-thought in reasoning_content\n- 128K context, 64K max output (default 32K)\n- Supports JSON mode, function calling\n- Pricing: $0.028/1M cache hit, $0.28/1M cache miss, $0.42/1M output\n\n## Tool: deepseek_chat\n\n### Input Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| messages | Array<{role, content, tool_call_id?}> | Yes | - | Conversation messages. Roles: system, user, assistant, tool. Content can be string or array of content parts (text/image_url) when multimodal is enabled |\n| model | \"deepseek-chat\" \\| \"deepseek-reasoner\" | No | \"deepseek-chat\" | Model to use |\n| temperature | number (0-2) | No | 1.0 | Sampling temperature. Ignored when thinking enabled |\n| max_tokens | number (1-65536) | No | model max | Max tokens: chat=8192, reasoner=65536. Both have 128K context |\n| stream | boolean | No | false | Enable streaming mode |\n| tools | Array (max 128) | No | - | Function calling tool definitions |\n| tool_choice | \"auto\" \\| \"none\" \\| \"required\" \\| {type, function} | No | \"auto\" | Which tool to call |\n| thinking | {type: \"enabled\" \\| \"disabled\"} | No | - | Enable thinking mode |\n| json_mode | boolean | No | false | Enable JSON output mode (supported by both models) |\n| session_id | string | No | - | Session ID for multi-turn conversations |\n\n### Multi-Turn Sessions\n\nUse `session_id` to maintain conversation context across requests:\n\n```json\n// First request - creates session\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What is the capital of France?\"}],\n \"session_id\": \"my-session\"\n}\n\n// Second request - previous context is automatically prepended\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What about Germany?\"}],\n \"session_id\": \"my-session\"\n}\n```\n\nSessions are stored in memory for the lifetime of the MCP server process. They expire after SESSION_TTL_MINUTES (default: 30). Omit session_id for stateless single-turn requests.\n\n### Thinking Mode\n\nEnable thinking mode on deepseek-chat:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Analyze quicksort complexity\"}],\n \"model\": \"deepseek-chat\",\n \"thinking\": {\"type\": \"enabled\"}\n}\n```\n\nWhen enabled, temperature/top_p/frequency_penalty/presence_penalty are automatically ignored with a logged warning. deepseek-reasoner has implicit thinking (always active).\n\n### JSON Output Mode\n\nGet structured JSON responses:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Return a json object with user data\"}],\n \"model\": \"deepseek-chat\",\n \"json_mode\": true\n}\n```\n\nInclude the word \"json\" in your prompt for best results. Supported by both models.\n\n### Tool Definition Format\n\n```json\n{\n \"type\": \"function\",\n \"function\": {\n \"name\": \"function_name\",\n \"description\": \"What the function does\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"param1\": { \"type\": \"string\", \"description\": \"...\" }\n },\n \"required\": [\"param1\"]\n }\n }\n}\n```\n\n### Response Format\n\n```json\n{\n \"content\": \"Response text\",\n \"reasoning_content\": \"Chain-of-thought (reasoner only)\",\n \"model\": \"deepseek-chat\",\n \"usage\": {\n \"prompt_tokens\": 10,\n \"completion_tokens\": 20,\n \"total_tokens\": 30,\n \"prompt_cache_hit_tokens\": 8,\n \"prompt_cache_miss_tokens\": 2\n },\n \"finish_reason\": \"stop\",\n \"tool_calls\": [\n {\n \"id\": \"call_abc123\",\n \"type\": \"function\",\n \"function\": { \"name\": \"fn_name\", \"arguments\": \"{\\\"key\\\":\\\"value\\\"}\" }\n }\n ],\n \"cost_usd\": 0.000012,\n \"session_id\": \"my-session\"\n}\n```\n\n## Tool: deepseek_sessions\n\nManage conversation sessions:\n\n```json\n{\"action\": \"list\"} // List all active sessions\n{\"action\": \"delete\", \"session_id\": \"my-session\"} // Delete specific session\n{\"action\": \"clear\"} // Clear all sessions\n```\n\n## MCP Resources\n\nThree read-only resources:\n\n| URI | Description |\n|-----|-------------|\n| deepseek://models | Model list with capabilities, context limits, pricing |\n| deepseek://config | Current server config (API key masked: sk-****1234) |\n| deepseek://usage | Real-time usage stats (requests, tokens, costs, sessions, cache ratio) |\n\n## Model Fallback & Circuit Breaker\n\n**Fallback**: On retryable errors (429, 503, timeout), automatically tries the other model:\n- deepseek-chat fails → tries deepseek-reasoner\n- deepseek-reasoner fails → tries deepseek-chat\n- Response includes fallback info when fallback was used\n\n**Circuit Breaker**: Protects against cascading failures:\n- CLOSED → normal operation\n- After CIRCUIT_BREAKER_THRESHOLD failures (default 5) → OPEN (fast-fail for CIRCUIT_BREAKER_RESET_TIMEOUT ms)\n- After timeout → HALF_OPEN (probe with 1 request)\n- Probe succeeds → CLOSED; Probe fails → OPEN again\n\nDisable with FALLBACK_ENABLED=false.\n\n## Function Calling Flow\n\n1. Send messages with `tools` array defining available functions\n2. Model responds with `tool_calls` containing function name and arguments\n3. Execute the function locally\n4. Send result back as a `tool` role message with `tool_call_id`\n5. Model generates final response using the tool result\n\n## Cost Tracking\n\nV3.2 provides cache-aware pricing. Cost display shows:\n- Total cost in USD\n- Cache hit ratio (percentage of prompt tokens served from cache)\n- Estimated savings from cache hits\n\nExample output: `$0.0042 (cache hit: 80%, saved ~$0.0168)`\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-chat | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n| CIRCUIT_BREAKER_THRESHOLD | 5 | Consecutive failures before circuit opens |\n| CIRCUIT_BREAKER_RESET_TIMEOUT | 30000 | Milliseconds before circuit half-opens |\n| MAX_SESSION_MESSAGES | 200 | Max messages per session (sliding window) |\n\n## Prompt Templates (12 total)\n\n### Core Reasoning\n- debug_with_reasoning(code, error?, language?)\n- code_review_deep(code, language?, focus: security|performance|quality|all)\n- research_synthesis(topic, context?, depth: brief|moderate|comprehensive)\n- strategic_planning(goal, context?, constraints?)\n- explain_like_im_five(topic, audience: child|beginner|intermediate)\n\n### Advanced\n- mathematical_proof(statement, context?)\n- argument_validation(argument, type: informal|formal|both)\n- creative_ideation(challenge, constraints?, quantity: 1-20)\n- cost_comparison(task, estimated_tokens)\n- pair_programming(task, language, style: beginner|intermediate|expert)\n\n### Function Calling\n- function_call_debug(tools_json, messages_json, error?)\n- create_function_schema(description, examples?)\n\n## Architecture\n\n```\nsrc/\n index.ts # Entry point, bootstrap\n server.ts # McpServer factory (version from package.json)\n deepseek-client.ts # DeepSeek API wrapper (circuit breaker + fallback)\n config.ts # Zod-validated config from env vars\n cost.ts # V3.2 cache-aware cost calculation\n schemas.ts # Zod input validation schemas\n types.ts # TypeScript types + type guards\n errors.ts # Custom error classes\n session.ts # In-memory session store\n circuit-breaker.ts # Circuit breaker pattern\n usage-tracker.ts # Usage statistics tracker\n tools/\n deepseek-chat.ts # deepseek_chat tool (sessions + fallback)\n deepseek-sessions.ts # deepseek_sessions tool\n index.ts # Tool registration aggregator\n resources/\n models.ts # deepseek://models resource\n config.ts # deepseek://config resource\n usage.ts # deepseek://usage resource\n index.ts # Resource registration aggregator\n prompts/\n core.ts # 5 core reasoning prompts\n advanced.ts # 5 advanced prompts\n function-calling.ts # 2 function calling prompts\n index.ts # Prompt registration aggregator\n```\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-chat | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n| CIRCUIT_BREAKER_THRESHOLD | 5 | Consecutive failures before circuit opens |\n| CIRCUIT_BREAKER_RESET_TIMEOUT | 30000 | Milliseconds before circuit half-opens |\n| MAX_SESSION_MESSAGES | 200 | Max messages per session (sliding window) |\n| ENABLE_MULTIMODAL | false | Enable multimodal (image) input support |\n\n## Tech Stack\n\n- TypeScript 5.7 (strict mode)\n- @modelcontextprotocol/sdk for MCP protocol\n- OpenAI SDK v6 for API compatibility\n- Zod v4 for validation\n- Vitest for testing (241 tests)\n- Node.js 18+\n\n## Links\n\n- npm: https://www.npmjs.com/package/@arikusi/deepseek-mcp-server\n- GitHub: https://github.com/arikusi/deepseek-mcp-server\n- DeepSeek API: https://api-docs.deepseek.com\n- MCP Spec: https://modelcontextprotocol.io\n"}, "d9a2fab03326ab6f722658de2c8b7461433f2ea9": {"t": 1785749510.6515439, "n": 10834, "h": "648d946d49f33533", "c": "# DeepSeek MCP Server - Complete Documentation\n\nA Model Context Protocol (MCP) server that integrates DeepSeek V3.2 AI models with MCP-compatible clients like Claude Code and Gemini CLI. Provides multi-turn sessions, model fallback with circuit breaker, MCP Resources, chat completion, thinking mode, JSON output, function calling, cache-aware cost tracking, and 12 prompt templates.\n\n## Installation\n\n```bash\n# Claude Code (all projects)\nclaude mcp add -s user deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Gemini CLI\ngemini mcp add deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Global npm install\nnpm install -g @arikusi/deepseek-mcp-server\n```\n\nGet API key: https://platform.deepseek.com\n\n## Models\n\nBoth models run DeepSeek-V3.2 with unified pricing.\n\n### deepseek-chat\n- General conversations, coding, content generation\n- Non-thinking mode (can enable thinking via parameter)\n- 128K context, 8K max output (default 4K)\n- Supports thinking mode, JSON mode, function calling, FIM completion\n- Pricing: $0.028/1M cache hit, $0.28/1M cache miss, $0.42/1M output\n\n### deepseek-reasoner\n- Complex reasoning, math, logic, multi-step tasks\n- Thinking mode (always active), shows chain-of-thought in reasoning_content\n- 128K context, 64K max output (default 32K)\n- Supports JSON mode, function calling\n- Pricing: $0.028/1M cache hit, $0.28/1M cache miss, $0.42/1M output\n\n## Tool: deepseek_chat\n\n### Input Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| messages | Array<{role, content, tool_call_id?}> | Yes | - | Conversation messages. Roles: system, user, assistant, tool |\n| model | \"deepseek-chat\" \\| \"deepseek-reasoner\" | No | \"deepseek-chat\" | Model to use |\n| temperature | number (0-2) | No | 1.0 | Sampling temperature. Ignored when thinking enabled |\n| max_tokens | number (1-65536) | No | model max | Max tokens: chat=8192, reasoner=65536. Both have 128K context |\n| stream | boolean | No | false | Enable streaming mode |\n| tools | Array (max 128) | No | - | Function calling tool definitions |\n| tool_choice | \"auto\" \\| \"none\" \\| \"required\" \\| {type, function} | No | \"auto\" | Which tool to call |\n| thinking | {type: \"enabled\" \\| \"disabled\"} | No | - | Enable thinking mode for enhanced reasoning |\n| json_mode | boolean | No | false | Enable JSON output mode (supported by both models) |\n| session_id | string | No | - | Session ID for multi-turn conversations |\n\n### Multi-Turn Sessions\n\nUse `session_id` to maintain conversation context across requests:\n\n```json\n// First request - creates session\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What is the capital of France?\"}],\n \"session_id\": \"my-session\"\n}\n\n// Second request - previous context is automatically prepended\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What about Germany?\"}],\n \"session_id\": \"my-session\"\n}\n```\n\nSessions are stored in memory for the lifetime of the MCP server process. They expire after SESSION_TTL_MINUTES (default: 30). Omit session_id for stateless single-turn requests.\n\n### Thinking Mode\n\nEnable enhanced reasoning on deepseek-chat:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Analyze quicksort complexity\"}],\n \"model\": \"deepseek-chat\",\n \"thinking\": {\"type\": \"enabled\"}\n}\n```\n\nWhen enabled, temperature/top_p/frequency_penalty/presence_penalty are automatically ignored with a logged warning. deepseek-reasoner has implicit thinking (always active).\n\n### JSON Output Mode\n\nGet structured JSON responses:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Return a json object with user data\"}],\n \"model\": \"deepseek-chat\",\n \"json_mode\": true\n}\n```\n\nInclude the word \"json\" in your prompt for best results. Supported by both models.\n\n### Tool Definition Format\n\n```json\n{\n \"type\": \"function\",\n \"function\": {\n \"name\": \"function_name\",\n \"description\": \"What the function does\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"param1\": { \"type\": \"string\", \"description\": \"...\" }\n },\n \"required\": [\"param1\"]\n }\n }\n}\n```\n\n### Response Format\n\n```json\n{\n \"content\": \"Response text\",\n \"reasoning_content\": \"Chain-of-thought (reasoner only)\",\n \"model\": \"deepseek-chat\",\n \"usage\": {\n \"prompt_tokens\": 10,\n \"completion_tokens\": 20,\n \"total_tokens\": 30,\n \"prompt_cache_hit_tokens\": 8,\n \"prompt_cache_miss_tokens\": 2\n },\n \"finish_reason\": \"stop\",\n \"tool_calls\": [\n {\n \"id\": \"call_abc123\",\n \"type\": \"function\",\n \"function\": { \"name\": \"fn_name\", \"arguments\": \"{\\\"key\\\":\\\"value\\\"}\" }\n }\n ],\n \"cost_usd\": 0.000012,\n \"session_id\": \"my-session\"\n}\n```\n\n## Tool: deepseek_sessions\n\nManage conversation sessions:\n\n```json\n{\"action\": \"list\"} // List all active sessions\n{\"action\": \"delete\", \"session_id\": \"my-session\"} // Delete specific session\n{\"action\": \"clear\"} // Clear all sessions\n```\n\n## MCP Resources\n\nThree read-only resources:\n\n| URI | Description |\n|-----|-------------|\n| deepseek://models | Model list with capabilities, context limits, pricing |\n| deepseek://config | Current server config (API key masked: sk-****1234) |\n| deepseek://usage | Real-time usage stats (requests, tokens, costs, sessions, cache ratio) |\n\n## Model Fallback & Circuit Breaker\n\n**Fallback**: On retryable errors (429, 503, timeout), automatically tries the other model:\n- deepseek-chat fails → tries deepseek-reasoner\n- deepseek-reasoner fails → tries deepseek-chat\n- Response includes fallback info when fallback was used\n\n**Circuit Breaker**: Protects against cascading failures:\n- CLOSED → normal operation\n- After CIRCUIT_BREAKER_THRESHOLD failures (default 5) → OPEN (fast-fail for CIRCUIT_BREAKER_RESET_TIMEOUT ms)\n- After timeout → HALF_OPEN (probe with 1 request)\n- Probe succeeds → CLOSED; Probe fails → OPEN again\n\nDisable with FALLBACK_ENABLED=false.\n\n## Function Calling Flow\n\n1. Send messages with `tools` array defining available functions\n2. Model responds with `tool_calls` containing function name and arguments\n3. Execute the function locally\n4. Send result back as a `tool` role message with `tool_call_id`\n5. Model generates final response using the tool result\n\n## Cost Tracking\n\nV3.2 provides cache-aware pricing. Cost display shows:\n- Total cost in USD\n- Cache hit ratio (percentage of prompt tokens served from cache)\n- Estimated savings from cache hits\n\nExample output: `$0.0042 (cache hit: 80%, saved ~$0.0168)`\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-chat | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n| CIRCUIT_BREAKER_THRESHOLD | 5 | Consecutive failures before circuit opens |\n| CIRCUIT_BREAKER_RESET_TIMEOUT | 30000 | Milliseconds before circuit half-opens |\n| MAX_SESSION_MESSAGES | 200 | Max messages per session (sliding window) |\n\n## Prompt Templates (12 total)\n\n### Core Reasoning\n- debug_with_reasoning(code, error?, language?)\n- code_review_deep(code, language?, focus: security|performance|quality|all)\n- research_synthesis(topic, context?, depth: brief|moderate|comprehensive)\n- strategic_planning(goal, context?, constraints?)\n- explain_like_im_five(topic, audience: child|beginner|intermediate)\n\n### Advanced\n- mathematical_proof(statement, context?)\n- argument_validation(argument, type: informal|formal|both)\n- creative_ideation(challenge, constraints?, quantity: 1-20)\n- cost_comparison(task, estimated_tokens)\n- pair_programming(task, language, style: beginner|intermediate|expert)\n\n### Function Calling\n- function_call_debug(tools_json, messages_json, error?)\n- create_function_schema(description, examples?)\n\n## Architecture\n\n```\nsrc/\n index.ts # Entry point, bootstrap\n server.ts # McpServer factory (version from package.json)\n deepseek-client.ts # DeepSeek API wrapper (circuit breaker + fallback)\n config.ts # Zod-validated config from env vars\n cost.ts # V3.2 cache-aware cost calculation\n schemas.ts # Zod input validation schemas\n types.ts # TypeScript types + type guards\n errors.ts # Custom error classes\n session.ts # In-memory session store\n circuit-breaker.ts # Circuit breaker pattern\n usage-tracker.ts # Usage statistics tracker\n tools/\n deepseek-chat.ts # deepseek_chat tool (sessions + fallback)\n deepseek-sessions.ts # deepseek_sessions tool\n index.ts # Tool registration aggregator\n resources/\n models.ts # deepseek://models resource\n config.ts # deepseek://config resource\n usage.ts # deepseek://usage resource\n index.ts # Resource registration aggregator\n prompts/\n core.ts # 5 core reasoning prompts\n advanced.ts # 5 advanced prompts\n function-calling.ts # 2 function calling prompts\n index.ts # Prompt registration aggregator\n```\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-chat | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n\n## Tech Stack\n\n- TypeScript 5.7 (strict mode)\n- @modelcontextprotocol/sdk for MCP protocol\n- OpenAI SDK v6 for API compatibility\n- Zod v4 for validation (14x faster parsing)\n- Vitest for testing (212 tests)\n- Node.js 18+\n\n## Links\n\n- npm: https://www.npmjs.com/package/@arikusi/deepseek-mcp-server\n- GitHub: https://github.com/arikusi/deepseek-mcp-server\n- DeepSeek API: https://api-docs.deepseek.com\n- MCP Spec: https://modelcontextprotocol.io\n"}, "014950567ad984191b0b4a34f539fbcf40551b41": {"t": 1785749511.759317, "n": 10470, "h": "f6c178d86504d728", "c": "# DeepSeek MCP Server - Complete Documentation\n\nA Model Context Protocol (MCP) server that integrates DeepSeek V3.2 AI models with MCP-compatible clients like Claude Code and Gemini CLI. Provides multi-turn sessions, model fallback with circuit breaker, MCP Resources, chat completion, thinking mode, JSON output, function calling, cache-aware cost tracking, and 12 prompt templates.\n\n## Installation\n\n```bash\n# Claude Code (all projects)\nclaude mcp add -s user deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Gemini CLI\ngemini mcp add deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Global npm install\nnpm install -g @arikusi/deepseek-mcp-server\n```\n\nGet API key: https://platform.deepseek.com\n\n## Models\n\nBoth models run DeepSeek-V3.2 with unified pricing.\n\n### deepseek-chat\n- General conversations, coding, content generation\n- Non-thinking mode (can enable thinking via parameter)\n- 128K context, 8K max output (default 4K)\n- Supports thinking mode, JSON mode, function calling, FIM completion\n- Pricing: $0.028/1M cache hit, $0.28/1M cache miss, $0.42/1M output\n\n### deepseek-reasoner\n- Complex reasoning, math, logic, multi-step tasks\n- Thinking mode (always active), shows chain-of-thought in reasoning_content\n- 128K context, 64K max output (default 32K)\n- Supports JSON mode, function calling\n- Pricing: $0.028/1M cache hit, $0.28/1M cache miss, $0.42/1M output\n\n## Tool: deepseek_chat\n\n### Input Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| messages | Array<{role, content, tool_call_id?}> | Yes | - | Conversation messages. Roles: system, user, assistant, tool |\n| model | \"deepseek-chat\" \\| \"deepseek-reasoner\" | No | \"deepseek-chat\" | Model to use |\n| temperature | number (0-2) | No | 1.0 | Sampling temperature. Ignored when thinking enabled |\n| max_tokens | number (1-65536) | No | model max | Max tokens: chat=8192, reasoner=65536. Both have 128K context |\n| stream | boolean | No | false | Enable streaming mode |\n| tools | Array (max 128) | No | - | Function calling tool definitions |\n| tool_choice | \"auto\" \\| \"none\" \\| \"required\" \\| {type, function} | No | \"auto\" | Which tool to call |\n| thinking | {type: \"enabled\" \\| \"disabled\"} | No | - | Enable thinking mode for enhanced reasoning |\n| json_mode | boolean | No | false | Enable JSON output mode (supported by both models) |\n| session_id | string | No | - | Session ID for multi-turn conversations |\n\n### Multi-Turn Sessions\n\nUse `session_id` to maintain conversation context across requests:\n\n```json\n// First request - creates session\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What is the capital of France?\"}],\n \"session_id\": \"my-session\"\n}\n\n// Second request - previous context is automatically prepended\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What about Germany?\"}],\n \"session_id\": \"my-session\"\n}\n```\n\nSessions are stored in memory for the lifetime of the MCP server process. They expire after SESSION_TTL_MINUTES (default: 30). Omit session_id for stateless single-turn requests.\n\n### Thinking Mode\n\nEnable enhanced reasoning on deepseek-chat:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Analyze quicksort complexity\"}],\n \"model\": \"deepseek-chat\",\n \"thinking\": {\"type\": \"enabled\"}\n}\n```\n\nWhen enabled, temperature/top_p/frequency_penalty/presence_penalty are automatically ignored with a logged warning. deepseek-reasoner has implicit thinking (always active).\n\n### JSON Output Mode\n\nGet structured JSON responses:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Return a json object with user data\"}],\n \"model\": \"deepseek-chat\",\n \"json_mode\": true\n}\n```\n\nInclude the word \"json\" in your prompt for best results. Supported by both models.\n\n### Tool Definition Format\n\n```json\n{\n \"type\": \"function\",\n \"function\": {\n \"name\": \"function_name\",\n \"description\": \"What the function does\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"param1\": { \"type\": \"string\", \"description\": \"...\" }\n },\n \"required\": [\"param1\"]\n }\n }\n}\n```\n\n### Response Format\n\n```json\n{\n \"content\": \"Response text\",\n \"reasoning_content\": \"Chain-of-thought (reasoner only)\",\n \"model\": \"deepseek-chat\",\n \"usage\": {\n \"prompt_tokens\": 10,\n \"completion_tokens\": 20,\n \"total_tokens\": 30,\n \"prompt_cache_hit_tokens\": 8,\n \"prompt_cache_miss_tokens\": 2\n },\n \"finish_reason\": \"stop\",\n \"tool_calls\": [\n {\n \"id\": \"call_abc123\",\n \"type\": \"function\",\n \"function\": { \"name\": \"fn_name\", \"arguments\": \"{\\\"key\\\":\\\"value\\\"}\" }\n }\n ],\n \"cost_usd\": 0.000012,\n \"session_id\": \"my-session\"\n}\n```\n\n## Tool: deepseek_sessions\n\nManage conversation sessions:\n\n```json\n{\"action\": \"list\"} // List all active sessions\n{\"action\": \"delete\", \"session_id\": \"my-session\"} // Delete specific session\n{\"action\": \"clear\"} // Clear all sessions\n```\n\n## MCP Resources\n\nThree read-only resources:\n\n| URI | Description |\n|-----|-------------|\n| deepseek://models | Model list with capabilities, context limits, pricing |\n| deepseek://config | Current server config (API key masked: sk-****1234) |\n| deepseek://usage | Real-time usage stats (requests, tokens, costs, sessions, cache ratio) |\n\n## Model Fallback & Circuit Breaker\n\n**Fallback**: On retryable errors (429, 503, timeout), automatically tries the other model:\n- deepseek-chat fails → tries deepseek-reasoner\n- deepseek-reasoner fails → tries deepseek-chat\n- Response includes fallback info when fallback was used\n\n**Circuit Breaker**: Protects against cascading failures:\n- CLOSED → normal operation\n- After 5 failures → OPEN (fast-fail for 30s)\n- After timeout → HALF_OPEN (probe with 1 request)\n- Probe succeeds → CLOSED; Probe fails → OPEN again\n\nDisable with FALLBACK_ENABLED=false.\n\n## Function Calling Flow\n\n1. Send messages with `tools` array defining available functions\n2. Model responds with `tool_calls` containing function name and arguments\n3. Execute the function locally\n4. Send result back as a `tool` role message with `tool_call_id`\n5. Model generates final response using the tool result\n\n## Cost Tracking\n\nV3.2 provides cache-aware pricing. Cost display shows:\n- Total cost in USD\n- Cache hit ratio (percentage of prompt tokens served from cache)\n- Estimated savings from cache hits\n\nExample output: `$0.0042 (cache hit: 80%, saved ~$0.0168)`\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n\n## Prompt Templates (12 total)\n\n### Core Reasoning\n- debug_with_reasoning(code, error?, language?)\n- code_review_deep(code, language?, focus: security|performance|quality|all)\n- research_synthesis(topic, context?, depth: brief|moderate|comprehensive)\n- strategic_planning(goal, context?, constraints?)\n- explain_like_im_five(topic, audience: child|beginner|intermediate)\n\n### Advanced\n- mathematical_proof(statement, context?)\n- argument_validation(argument, type: informal|formal|both)\n- creative_ideation(challenge, constraints?, quantity: 1-20)\n- cost_comparison(task, estimated_tokens)\n- pair_programming(task, language, style: beginner|intermediate|expert)\n\n### Function Calling\n- function_call_debug(tools_json, messages_json, error?)\n- create_function_schema(description, examples?)\n\n## Architecture\n\n```\nsrc/\n index.ts # Entry point, bootstrap\n server.ts # McpServer factory (version from package.json)\n deepseek-client.ts # DeepSeek API wrapper (circuit breaker + fallback)\n config.ts # Zod-validated config from env vars\n cost.ts # V3.2 cache-aware cost calculation\n schemas.ts # Zod input validation schemas\n types.ts # TypeScript types + type guards\n errors.ts # Custom error classes\n session.ts # In-memory session store\n circuit-breaker.ts # Circuit breaker pattern\n usage-tracker.ts # Usage statistics tracker\n tools/\n deepseek-chat.ts # deepseek_chat tool (sessions + fallback)\n deepseek-sessions.ts # deepseek_sessions tool\n index.ts # Tool registration aggregator\n resources/\n models.ts # deepseek://models resource\n config.ts # deepseek://config resource\n usage.ts # deepseek://usage resource\n index.ts # Resource registration aggregator\n prompts/\n core.ts # 5 core reasoning prompts\n advanced.ts # 5 advanced prompts\n function-calling.ts # 2 function calling prompts\n index.ts # Prompt registration aggregator\n```\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| DEFAULT_MODEL | deepseek-chat | Default model for requests |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n\n## Tech Stack\n\n- TypeScript 5.7 (strict mode)\n- @modelcontextprotocol/sdk for MCP protocol\n- OpenAI SDK v6 for API compatibility\n- Zod v4 for validation (14x faster parsing)\n- Vitest for testing (208 tests)\n- Node.js 18+\n\n## Links\n\n- npm: https://www.npmjs.com/package/@arikusi/deepseek-mcp-server\n- GitHub: https://github.com/arikusi/deepseek-mcp-server\n- DeepSeek API: https://api-docs.deepseek.com\n- MCP Spec: https://modelcontextprotocol.io\n"}, "d08fbeadacd6deb176fba497c16e216536124a8d": {"t": 1785749513.0391765, "n": 9679, "h": "bb71653c1c0f1848", "c": "# DeepSeek MCP Server - Complete Documentation\n\nA Model Context Protocol (MCP) server that integrates DeepSeek V3.2 AI models with MCP-compatible clients like Claude Code and Gemini CLI. Provides multi-turn sessions, model fallback with circuit breaker, MCP Resources, chat completion, thinking mode, JSON output, function calling, cache-aware cost tracking, and 12 prompt templates.\n\n## Installation\n\n```bash\n# Claude Code (all projects)\nclaude mcp add -s user deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Gemini CLI\ngemini mcp add deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Global npm install\nnpm install -g @arikusi/deepseek-mcp-server\n```\n\nGet API key: https://platform.deepseek.com\n\n## Models\n\nBoth models run DeepSeek-V3.2 with unified pricing.\n\n### deepseek-chat\n- General conversations, coding, content generation\n- Non-thinking mode (can enable thinking via parameter)\n- 128K context, 8K max output (default 4K)\n- Supports thinking mode, JSON mode, function calling, FIM completion\n- Pricing: $0.028/1M cache hit, $0.28/1M cache miss, $0.42/1M output\n\n### deepseek-reasoner\n- Complex reasoning, math, logic, multi-step tasks\n- Thinking mode (always active), shows chain-of-thought in reasoning_content\n- 128K context, 64K max output (default 32K)\n- Supports JSON mode, function calling\n- Pricing: $0.028/1M cache hit, $0.28/1M cache miss, $0.42/1M output\n\n## Tool: deepseek_chat\n\n### Input Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| messages | Array<{role, content, tool_call_id?}> | Yes | - | Conversation messages. Roles: system, user, assistant, tool |\n| model | \"deepseek-chat\" \\| \"deepseek-reasoner\" | No | \"deepseek-chat\" | Model to use |\n| temperature | number (0-2) | No | 1.0 | Sampling temperature. Ignored when thinking enabled |\n| max_tokens | number (1-65536) | No | model max | Max tokens: chat=8192, reasoner=65536. Both have 128K context |\n| stream | boolean | No | false | Enable streaming mode |\n| tools | Array (max 128) | No | - | Function calling tool definitions |\n| tool_choice | \"auto\" \\| \"none\" \\| \"required\" \\| {type, function} | No | \"auto\" | Which tool to call |\n| thinking | {type: \"enabled\" \\| \"disabled\"} | No | - | Enable thinking mode for enhanced reasoning |\n| json_mode | boolean | No | false | Enable JSON output mode (supported by both models) |\n| session_id | string | No | - | Session ID for multi-turn conversations |\n\n### Multi-Turn Sessions\n\nUse `session_id` to maintain conversation context across requests:\n\n```json\n// First request - creates session\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What is the capital of France?\"}],\n \"session_id\": \"my-session\"\n}\n\n// Second request - previous context is automatically prepended\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"What about Germany?\"}],\n \"session_id\": \"my-session\"\n}\n```\n\nSessions are stored in memory for the lifetime of the MCP server process. They expire after SESSION_TTL_MINUTES (default: 30). Omit session_id for stateless single-turn requests.\n\n### Thinking Mode\n\nEnable enhanced reasoning on deepseek-chat:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Analyze quicksort complexity\"}],\n \"model\": \"deepseek-chat\",\n \"thinking\": {\"type\": \"enabled\"}\n}\n```\n\nWhen enabled, temperature/top_p/frequency_penalty/presence_penalty are automatically ignored with a logged warning. deepseek-reasoner has implicit thinking (always active).\n\n### JSON Output Mode\n\nGet structured JSON responses:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Return a json object with user data\"}],\n \"model\": \"deepseek-chat\",\n \"json_mode\": true\n}\n```\n\nInclude the word \"json\" in your prompt for best results. Supported by both models.\n\n### Tool Definition Format\n\n```json\n{\n \"type\": \"function\",\n \"function\": {\n \"name\": \"function_name\",\n \"description\": \"What the function does\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"param1\": { \"type\": \"string\", \"description\": \"...\" }\n },\n \"required\": [\"param1\"]\n }\n }\n}\n```\n\n### Response Format\n\n```json\n{\n \"content\": \"Response text\",\n \"reasoning_content\": \"Chain-of-thought (reasoner only)\",\n \"model\": \"deepseek-chat\",\n \"usage\": {\n \"prompt_tokens\": 10,\n \"completion_tokens\": 20,\n \"total_tokens\": 30,\n \"prompt_cache_hit_tokens\": 8,\n \"prompt_cache_miss_tokens\": 2\n },\n \"finish_reason\": \"stop\",\n \"tool_calls\": [\n {\n \"id\": \"call_abc123\",\n \"type\": \"function\",\n \"function\": { \"name\": \"fn_name\", \"arguments\": \"{\\\"key\\\":\\\"value\\\"}\" }\n }\n ],\n \"cost_usd\": 0.000012,\n \"session_id\": \"my-session\"\n}\n```\n\n## Tool: deepseek_sessions\n\nManage conversation sessions:\n\n```json\n{\"action\": \"list\"} // List all active sessions\n{\"action\": \"delete\", \"session_id\": \"my-session\"} // Delete specific session\n{\"action\": \"clear\"} // Clear all sessions\n```\n\n## MCP Resources\n\nThree read-only resources:\n\n| URI | Description |\n|-----|-------------|\n| deepseek://models | Model list with capabilities, context limits, pricing |\n| deepseek://config | Current server config (API key masked: sk-****1234) |\n| deepseek://usage | Real-time usage stats (requests, tokens, costs, sessions, cache ratio) |\n\n## Model Fallback & Circuit Breaker\n\n**Fallback**: On retryable errors (429, 503, timeout), automatically tries the other model:\n- deepseek-chat fails → tries deepseek-reasoner\n- deepseek-reasoner fails → tries deepseek-chat\n- Response includes fallback info when fallback was used\n\n**Circuit Breaker**: Protects against cascading failures:\n- CLOSED → normal operation\n- After 5 failures → OPEN (fast-fail for 30s)\n- After timeout → HALF_OPEN (probe with 1 request)\n- Probe succeeds → CLOSED; Probe fails → OPEN again\n\nDisable with FALLBACK_ENABLED=false.\n\n## Function Calling Flow\n\n1. Send messages with `tools` array defining available functions\n2. Model responds with `tool_calls` containing function name and arguments\n3. Execute the function locally\n4. Send result back as a `tool` role message with `tool_call_id`\n5. Model generates final response using the tool result\n\n## Cost Tracking\n\nV3.2 provides cache-aware pricing. Cost display shows:\n- Total cost in USD\n- Cache hit ratio (percentage of prompt tokens served from cache)\n- Estimated savings from cache hits\n\nExample output: `$0.0042 (cache hit: 80%, saved ~$0.0168)`\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n| SESSION_TTL_MINUTES | 30 | Session time-to-live in minutes |\n| MAX_SESSIONS | 100 | Maximum concurrent sessions |\n| FALLBACK_ENABLED | true | Enable automatic model fallback |\n\n## Prompt Templates (12 total)\n\n### Core Reasoning\n- debug_with_reasoning(code, error?, language?)\n- code_review_deep(code, language?, focus: security|performance|quality|all)\n- research_synthesis(topic, context?, depth: brief|moderate|comprehensive)\n- strategic_planning(goal, context?, constraints?)\n- explain_like_im_five(topic, audience: child|beginner|intermediate)\n\n### Advanced\n- mathematical_proof(statement, context?)\n- argument_validation(argument, type: informal|formal|both)\n- creative_ideation(challenge, constraints?, quantity: 1-20)\n- cost_comparison(task, estimated_tokens)\n- pair_programming(task, language, style: beginner|intermediate|expert)\n\n### Function Calling\n- function_call_debug(tools_json, messages_json, error?)\n- create_function_schema(description, examples?)\n\n## Architecture\n\n```\nsrc/\n index.ts # Entry point, bootstrap\n server.ts # McpServer factory (version from package.json)\n deepseek-client.ts # DeepSeek API wrapper (circuit breaker + fallback)\n config.ts # Zod-validated config from env vars\n cost.ts # V3.2 cache-aware cost calculation\n schemas.ts # Zod input validation schemas\n types.ts # TypeScript types + type guards\n errors.ts # Custom error classes\n session.ts # In-memory session store\n circuit-breaker.ts # Circuit breaker pattern\n usage-tracker.ts # Usage statistics tracker\n tools/\n deepseek-chat.ts # deepseek_chat tool (sessions + fallback)\n deepseek-sessions.ts # deepseek_sessions tool\n index.ts # Tool registration aggregator\n resources/\n models.ts # deepseek://models resource\n config.ts # deepseek://config resource\n usage.ts # deepseek://usage resource\n index.ts # Resource registration aggregator\n prompts/\n core.ts # 5 core reasoning prompts\n advanced.ts # 5 advanced prompts\n function-calling.ts # 2 function calling prompts\n index.ts # Prompt registration aggregator\n```\n\n## Tech Stack\n\n- TypeScript 5.7 (strict mode)\n- @modelcontextprotocol/sdk for MCP protocol\n- OpenAI SDK for API compatibility\n- Zod for validation\n- Vitest for testing (198 tests)\n- Node.js 18+\n\n## Links\n\n- npm: https://www.npmjs.com/package/@arikusi/deepseek-mcp-server\n- GitHub: https://github.com/arikusi/deepseek-mcp-server\n- DeepSeek API: https://api-docs.deepseek.com\n- MCP Spec: https://modelcontextprotocol.io\n"}, "e6911bcd8cbeaa4ba2ac17e1328a5ffcb6af64fa": {"t": 1785749514.1440568, "n": 7516, "h": "810d499a26aee370", "c": "# DeepSeek MCP Server - Complete Documentation\n\nA Model Context Protocol (MCP) server that integrates DeepSeek V3.2 AI models with MCP-compatible clients like Claude Code and Gemini CLI. Provides chat completion, thinking mode, JSON output, function calling, cache-aware cost tracking, and 12 prompt templates via a single `deepseek_chat` tool.\n\n## Installation\n\n```bash\n# Claude Code (all projects)\nclaude mcp add -s user deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Gemini CLI\ngemini mcp add deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Global npm install\nnpm install -g @arikusi/deepseek-mcp-server\n```\n\nGet API key: https://platform.deepseek.com\n\n## Models\n\nBoth models run DeepSeek-V3.2 with unified pricing.\n\n### deepseek-chat\n- General conversations, coding, content generation\n- Non-thinking mode (can enable thinking via parameter)\n- 128K context, 8K max output (default 4K)\n- Supports thinking mode, JSON mode, function calling, FIM completion\n- Pricing: $0.028/1M cache hit, $0.28/1M cache miss, $0.42/1M output\n\n### deepseek-reasoner\n- Complex reasoning, math, logic, multi-step tasks\n- Thinking mode (always active), shows chain-of-thought in reasoning_content\n- 128K context, 64K max output (default 32K)\n- Supports JSON mode, function calling\n- Pricing: $0.028/1M cache hit, $0.28/1M cache miss, $0.42/1M output\n\n## Tool: deepseek_chat\n\n### Input Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| messages | Array<{role, content, tool_call_id?}> | Yes | - | Conversation messages. Roles: system, user, assistant, tool |\n| model | \"deepseek-chat\" \\| \"deepseek-reasoner\" | No | \"deepseek-chat\" | Model to use |\n| temperature | number (0-2) | No | 1.0 | Sampling temperature. Ignored when thinking enabled |\n| max_tokens | number (1-65536) | No | model max | Max tokens: chat=8192, reasoner=65536. Both have 128K context |\n| stream | boolean | No | false | Enable streaming mode |\n| tools | Array (max 128) | No | - | Function calling tool definitions |\n| tool_choice | \"auto\" \\| \"none\" \\| \"required\" \\| {type, function} | No | \"auto\" | Which tool to call |\n| thinking | {type: \"enabled\" \\| \"disabled\"} | No | - | Enable thinking mode for enhanced reasoning |\n| json_mode | boolean | No | false | Enable JSON output mode (supported by both models) |\n\n### Thinking Mode\n\nEnable enhanced reasoning on deepseek-chat:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Analyze quicksort complexity\"}],\n \"model\": \"deepseek-chat\",\n \"thinking\": {\"type\": \"enabled\"}\n}\n```\n\nWhen enabled, temperature/top_p/frequency_penalty/presence_penalty are automatically ignored with a logged warning. deepseek-reasoner has implicit thinking (always active).\n\n### JSON Output Mode\n\nGet structured JSON responses:\n\n```json\n{\n \"messages\": [{\"role\": \"user\", \"content\": \"Return a json object with user data\"}],\n \"model\": \"deepseek-chat\",\n \"json_mode\": true\n}\n```\n\nInclude the word \"json\" in your prompt for best results. Supported by both models.\n\n### Tool Definition Format\n\n```json\n{\n \"type\": \"function\",\n \"function\": {\n \"name\": \"function_name\",\n \"description\": \"What the function does\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"param1\": { \"type\": \"string\", \"description\": \"...\" }\n },\n \"required\": [\"param1\"]\n }\n }\n}\n```\n\n### Response Format\n\n```json\n{\n \"content\": \"Response text\",\n \"reasoning_content\": \"Chain-of-thought (reasoner only)\",\n \"model\": \"deepseek-chat\",\n \"usage\": {\n \"prompt_tokens\": 10,\n \"completion_tokens\": 20,\n \"total_tokens\": 30,\n \"prompt_cache_hit_tokens\": 8,\n \"prompt_cache_miss_tokens\": 2\n },\n \"finish_reason\": \"stop\",\n \"tool_calls\": [\n {\n \"id\": \"call_abc123\",\n \"type\": \"function\",\n \"function\": { \"name\": \"fn_name\", \"arguments\": \"{\\\"key\\\":\\\"value\\\"}\" }\n }\n ],\n \"cost_usd\": 0.000012\n}\n```\n\n## Function Calling Flow\n\n1. Send messages with `tools` array defining available functions\n2. Model responds with `tool_calls` containing function name and arguments\n3. Execute the function locally\n4. Send result back as a `tool` role message with `tool_call_id`\n5. Model generates final response using the tool result\n\nExample:\n```json\n{\n \"messages\": [\n {\"role\": \"user\", \"content\": \"Weather in NYC?\"}\n ],\n \"tools\": [{\n \"type\": \"function\",\n \"function\": {\n \"name\": \"get_weather\",\n \"parameters\": {\"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}}\n }\n }]\n}\n```\n\n## Cost Tracking\n\nV3.2 provides cache-aware pricing. Cost display shows:\n- Total cost in USD\n- Cache hit ratio (percentage of prompt tokens served from cache)\n- Estimated savings from cache hits\n\nExample output: `$0.0042 (cache hit: 80%, saved ~$0.0168)`\n\nIf the API doesn't return cache fields (older responses), all input is treated as cache miss — no breaking change.\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n\n## Prompt Templates (12 total)\n\n### Core Reasoning\n- debug_with_reasoning(code, error?, language?)\n- code_review_deep(code, language?, focus: security|performance|quality|all)\n- research_synthesis(topic, context?, depth: brief|moderate|comprehensive)\n- strategic_planning(goal, context?, constraints?)\n- explain_like_im_five(topic, audience: child|beginner|intermediate)\n\n### Advanced\n- mathematical_proof(statement, context?)\n- argument_validation(argument, type: informal|formal|both)\n- creative_ideation(challenge, constraints?, quantity: 1-20)\n- cost_comparison(task, estimated_tokens)\n- pair_programming(task, language, style: beginner|intermediate|expert)\n\n### Function Calling\n- function_call_debug(tools_json, messages_json, error?)\n- create_function_schema(description, examples?)\n\n## Architecture\n\n```\nsrc/\n index.ts # Entry point, bootstrap\n server.ts # McpServer factory (version from package.json)\n deepseek-client.ts # DeepSeek API wrapper (OpenAI SDK)\n config.ts # Zod-validated config from env vars\n cost.ts # V3.2 cache-aware cost calculation\n schemas.ts # Zod input validation schemas\n types.ts # TypeScript types + type guards\n errors.ts # Custom error classes (ConfigError, ApiError, etc.)\n tools/\n deepseek-chat.ts # deepseek_chat tool handler\n index.ts # Tool registration aggregator\n prompts/\n core.ts # 5 core reasoning prompts\n advanced.ts # 5 advanced prompts\n function-calling.ts # 2 function calling prompts\n index.ts # Prompt registration aggregator\n```\n\n## Tech Stack\n\n- TypeScript 5.7 (strict mode)\n- @modelcontextprotocol/sdk for MCP protocol\n- OpenAI SDK for API compatibility\n- Zod for validation\n- Vitest for testing (150 tests)\n- Node.js 18+\n\n## Links\n\n- npm: https://www.npmjs.com/package/@arikusi/deepseek-mcp-server\n- GitHub: https://github.com/arikusi/deepseek-mcp-server\n- DeepSeek API: https://api-docs.deepseek.com\n- MCP Spec: https://modelcontextprotocol.io\n"}, "440b7099f12fcab015d490983725d13e305ddc76": {"t": 1785749515.2518065, "n": 5783, "h": "5f48ba922d1f95a8", "c": "# DeepSeek MCP Server - Complete Documentation\n\nA Model Context Protocol (MCP) server that integrates DeepSeek AI models with MCP-compatible clients like Claude Code and Gemini CLI. Provides chat completion, function calling, cost tracking, and 12 prompt templates via a single `deepseek_chat` tool.\n\n## Installation\n\n```bash\n# Claude Code (all projects)\nclaude mcp add -s user deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Gemini CLI\ngemini mcp add deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key\n\n# Global npm install\nnpm install -g @arikusi/deepseek-mcp-server\n```\n\nGet API key: https://platform.deepseek.com\n\n## Models\n\n### deepseek-chat\n- General conversations, coding, content generation\n- Fast, 64K context, most economical\n- Pricing: $0.14/1M prompt + $0.28/1M completion tokens\n\n### deepseek-reasoner (R1)\n- Complex reasoning, math, logic, multi-step tasks\n- Shows chain-of-thought in reasoning_content field\n- 64K context\n- Pricing: $0.55/1M prompt + $2.19/1M completion tokens\n\n## Tool: deepseek_chat\n\n### Input Parameters\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| messages | Array<{role, content, tool_call_id?}> | Yes | - | Conversation messages. Roles: system, user, assistant, tool |\n| model | \"deepseek-chat\" \\| \"deepseek-reasoner\" | No | \"deepseek-chat\" | Model to use |\n| temperature | number (0-2) | No | 1.0 | Sampling temperature |\n| max_tokens | number (1-32768) | No | model max | Maximum tokens to generate |\n| stream | boolean | No | false | Enable streaming mode |\n| tools | Array (max 128) | No | - | Function calling tool definitions |\n| tool_choice | \"auto\" \\| \"none\" \\| \"required\" \\| {type, function} | No | \"auto\" | Which tool to call |\n\n### Tool Definition Format\n\n```json\n{\n \"type\": \"function\",\n \"function\": {\n \"name\": \"function_name\",\n \"description\": \"What the function does\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"param1\": { \"type\": \"string\", \"description\": \"...\" }\n },\n \"required\": [\"param1\"]\n }\n }\n}\n```\n\n### Response Format\n\n```json\n{\n \"content\": \"Response text\",\n \"reasoning_content\": \"Chain-of-thought (reasoner only)\",\n \"model\": \"deepseek-chat\",\n \"usage\": {\n \"prompt_tokens\": 10,\n \"completion_tokens\": 20,\n \"total_tokens\": 30\n },\n \"finish_reason\": \"stop\",\n \"tool_calls\": [\n {\n \"id\": \"call_abc123\",\n \"type\": \"function\",\n \"function\": { \"name\": \"fn_name\", \"arguments\": \"{\\\"key\\\":\\\"value\\\"}\" }\n }\n ],\n \"cost_usd\": 0.000012\n}\n```\n\n## Function Calling Flow\n\n1. Send messages with `tools` array defining available functions\n2. Model responds with `tool_calls` containing function name and arguments\n3. Execute the function locally\n4. Send result back as a `tool` role message with `tool_call_id`\n5. Model generates final response using the tool result\n\nExample:\n```json\n{\n \"messages\": [\n {\"role\": \"user\", \"content\": \"Weather in NYC?\"}\n ],\n \"tools\": [{\n \"type\": \"function\",\n \"function\": {\n \"name\": \"get_weather\",\n \"parameters\": {\"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}}\n }\n }]\n}\n```\n\n## Configuration (Environment Variables)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| DEEPSEEK_API_KEY | (required) | DeepSeek API key |\n| DEEPSEEK_BASE_URL | https://api.deepseek.com | Custom API endpoint |\n| SHOW_COST_INFO | true | Show cost info in responses |\n| REQUEST_TIMEOUT | 60000 | Request timeout (ms) |\n| MAX_RETRIES | 2 | Max retry count |\n| SKIP_CONNECTION_TEST | false | Skip startup API connection test |\n| MAX_MESSAGE_LENGTH | 100000 | Max message content length (chars) |\n\n## Prompt Templates (12 total)\n\n### Core Reasoning\n- debug_with_reasoning(code, error?, language?)\n- code_review_deep(code, language?, focus: security|performance|quality|all)\n- research_synthesis(topic, context?, depth: brief|moderate|comprehensive)\n- strategic_planning(goal, context?, constraints?)\n- explain_like_im_five(topic, audience: child|beginner|intermediate)\n\n### Advanced\n- mathematical_proof(statement, context?)\n- argument_validation(argument, type: informal|formal|both)\n- creative_ideation(challenge, constraints?, quantity: 1-20)\n- cost_comparison(task, estimated_tokens)\n- pair_programming(task, language, style: beginner|intermediate|expert)\n\n### Function Calling\n- function_call_debug(tools_json, messages_json, error?)\n- create_function_schema(description, examples?)\n\n## Architecture\n\n```\nsrc/\n index.ts # Entry point, bootstrap\n server.ts # McpServer factory (version from package.json)\n deepseek-client.ts # DeepSeek API wrapper (OpenAI SDK)\n config.ts # Zod-validated config from env vars\n cost.ts # Cost calculation per model\n schemas.ts # Zod input validation schemas\n types.ts # TypeScript types + type guards\n errors.ts # Custom error classes (ConfigError, ApiError, etc.)\n tools/\n deepseek-chat.ts # deepseek_chat tool handler\n index.ts # Tool registration aggregator\n prompts/\n core.ts # 5 core reasoning prompts\n advanced.ts # 5 advanced prompts\n function-calling.ts # 2 function calling prompts\n index.ts # Prompt registration aggregator\n```\n\n## Tech Stack\n\n- TypeScript 5.7 (strict mode)\n- @modelcontextprotocol/sdk for MCP protocol\n- OpenAI SDK for API compatibility\n- Zod for validation\n- Vitest for testing (126 tests)\n- Node.js 18+\n\n## Links\n\n- npm: https://www.npmjs.com/package/@arikusi/deepseek-mcp-server\n- GitHub: https://github.com/arikusi/deepseek-mcp-server\n- DeepSeek API: https://api-docs.deepseek.com\n- MCP Spec: https://modelcontextprotocol.io\n"}}}}