DeepSeek V4: deepseek-v4-flash and deepseek-v4-pro, both with 1M context and optional chain-of-thought thinking mode
Multi-Turn Sessions: Conversation context preserved across requests via session_id parameter
Model Fallback & Circuit Breaker: Automatic fallback between models with circuit breaker protection against cascading failures
MCP Resources: deepseek://models, deepseek://config, deepseek://usage — query model info, config, and usage stats
Thinking Mode: Enable chain-of-thought reasoning on either V4 model with thinking: {type: "enabled"}
JSON Output Mode: Structured JSON responses with json_mode: true
Schema-Validated JSON: Pass a response_schema and the server validates the output against it, with bounded repair retries and a ReDoS guard on schema patterns
Function Calling: OpenAI-compatible tool use with up to 128 tool definitions
Fill-in-the-Middle (FIM): Code and content completion between a prefix and suffix via the deepseek_fim tool
Cache-Aware Cost Tracking: Automatic cost calculation with cache hit/miss breakdown
Session Management Tool: List, delete, and clear sessions via deepseek_sessions tool
Configurable: Environment-based configuration with validation
12 Prompt Templates: Templates for debugging, code review, function calling, and more
Streaming Support: Real-time response generation
Multimodal Ready: Content part types for text + image input (enable with ENABLE_MULTIMODAL=true)
Remote Endpoint: Hosted at deepseek-mcp.tahirl.com/mcp — BYOK (Bring Your Own Key), no install needed
HTTP Transport: Self-hosted remote access via Streamable HTTP with TRANSPORT=http
Docker Ready: Multi-stage Dockerfile with health checks for containerized deployment
Tested: 340 tests, ~92% line coverage
Type-Safe: Full TypeScript implementation
MCP Compatible: Works with any MCP-compatible CLI (Claude Code, Gemini CLI, etc.)
Claude Code: ~/.claude.json (add to projects["your-project-path"].mcpServers section)
Other MCP clients: Check your client's documentation for config file location
Available Tools
deepseek_chat
Chat with DeepSeek AI models with automatic cost tracking and function calling support.
Parameters:
messages (required): Array of conversation messages
role: "system" | "user" | "assistant" | "tool"
content: Message text
tool_call_id (optional): Required for tool role messages
model (optional): "deepseek-v4-flash" (default) or "deepseek-v4-pro". The deprecated "deepseek-chat" and "deepseek-reasoner" aliases are still accepted and resolve to v4-flash (non-thinking / thinking); prefer the V4 names.
temperature (optional): 0-2, controls randomness (default: 1.0). Ignored when thinking mode is enabled.
max_tokens (optional): Maximum tokens to generate (V4 models support up to 384000)
thinking (optional): Toggle thinking mode, {type: "enabled"} to reason or {type: "disabled"} for a fast answer (non-thinking is the default)
reasoning_effort (optional): "high" (default) or "max", applies only while thinking mode is active
json_mode (optional): Enable JSON output mode (supported by both models)
response_schema (optional): A JSON Schema to validate the model output against. Implies JSON output. The server validates the parsed result and, on failure, issues up to RESPONSE_SCHEMA_MAX_RETRIES repair retries (default 2, set 0 to disable) that feed the validation error back to the model. Schema regex patterns are screened for ReDoS and an unsafe pattern is rejected up front.
session_id (optional): Session ID for multi-turn conversations. Previous context is automatically prepended.
Response includes:
Content with formatting (recovered as clean JSON when JSON output is requested)
Function call results (if tools were used)
Request information (tokens, model, cost in USD)
structuredContent.request: a self-contained per-request usage and cost summary (token counts, cache hit/miss, cost_usd), aggregated across any repair retries
structuredContent.effective and fallback: what was actually sent after alias/thinking resolution, and any silent model fallback that fired
structuredContent.schema: when response_schema is used, {valid, attempts, error?}; json_parse_error when JSON output could not be recovered
Example:
{
"messages": [
{
"role": "user",
"content": "Explain the theory of relativity in simple terms"
}
],
"model": "deepseek-v4-flash",
"temperature": 0.7,
"max_tokens": 1000
}
Reasoning Example (v4-flash with thinking enabled):
{
"messages": [
{
"role": "user",
"content": "If I have 10 apples and eat 3, then buy 5 more, how many do I have?"
}
],
"model": "deepseek-v4-flash",
"thinking": { "type": "enabled" }
}
Thinking mode returns the chain-of-thought in <thinking> tags followed by the final answer.
DeepSeek V4 Pro Example (hardest tasks):
{
"messages": [
{
"role": "user",
"content": "Prove that the square root of 2 is irrational."
}
],
"model": "deepseek-v4-pro",
"thinking": { "type": "enabled" }
}
When the model decides to call a function, the response includes tool_calls with the function name and arguments. You can then send the result back using a tool role message with the matching tool_call_id.
Thinking Mode Example:
{
"messages": [
{
"role": "user",
"content": "Analyze the time complexity of quicksort"
}
],
"model": "deepseek-v4-flash",
"thinking": { "type": "enabled" }
}
When thinking mode is enabled, temperature and top_p are automatically ignored.
JSON Output Mode Example:
{
"messages": [
{
"role": "user",
"content": "Return a json object with name, age, and city fields for a sample user"
}
],
"model": "deepseek-v4-flash",
"json_mode": true
}
JSON mode ensures the model outputs valid JSON. Include the word "json" in your prompt for best results. Supported by all models.
The server validates the parsed output against the schema. If it does not match, it retries up to RESPONSE_SCHEMA_MAX_RETRIES times (default 2), feeding the validation error back to the model, and returns the first schema-valid object. A persistent mismatch is surfaced as structuredContent.schema.valid = false rather than a silently coerced answer. Regex patterns in the schema are screened for catastrophic backtracking (ReDoS); an unsafe pattern is rejected up front as an invalid schema.
Multi-Turn Session Example:
{
"messages": [
{
"role": "user",
"content": "What is the capital of France?"
}
],
"session_id": "my-session-1"
}
Use the same session_id across requests to maintain conversation context. Messages are stored in memory and prepended automatically. In HTTP transport each connected MCP session has its own isolated session store — a session_id created by one HTTP client is not visible to another (see HTTP Transport below).
deepseek_fim
Fill-in-the-Middle completion. You give a prompt (the prefix) and an optional suffix, and the model completes the text in between. It is built for code completion and content infilling rather than conversation. FIM runs on DeepSeek's Beta endpoint in non-thinking mode, and the API caps output at 4096 tokens.
Parameters:
prompt (required): The prefix text before the gap. For code completion, this is the code up to the cursor.
suffix (optional): The text after the gap. The model fills the space between prompt and suffix.
model (optional): "deepseek-v4-flash" (default) or "deepseek-v4-pro". The deprecated "deepseek-chat" and "deepseek-reasoner" aliases are still accepted and resolve to v4-flash (FIM has no thinking mode).
max_tokens (optional): Maximum tokens to generate, up to 4096.
temperature (optional): 0-2, controls randomness (default: 1.0).
stop (optional): A stop string or an array of up to 16 stop strings.
Response includes:
The completion text
Request information (tokens, model, cost in USD)
Structured data with text, usage, finish_reason, and cost_usd fields
Example (code completion):
{
"prompt": "def fib(n):\n if n < 2:\n return n\n return ",
"suffix": "\n\nprint(fib(10))",
"model": "deepseek-v4-flash",
"max_tokens": 64
}
The model returns the missing middle, e.g. fib(n-1) + fib(n-2), using both the prefix and the suffix as context. Available on both the npm/stdio server and the hosted worker endpoint.
deepseek_sessions
Manage conversation sessions.
Parameters:
action (required): "list" | "clear" | "delete"
session_id (optional): Required when action is "delete"
argument_validation: Analyze arguments for logical fallacies
creative_ideation: Generate creative ideas with feasibility analysis
cost_comparison: Compare LLM costs for tasks
pair_programming: Interactive coding with explanations
Function Calling
function_call_debug: Debug function calling issues with tool definitions and messages
create_function_schema: Generate JSON Schema for function calling from natural language
Each prompt is optimized for thinking mode (v4-flash with thinking: {type: "enabled"}) to provide detailed reasoning.
Models
Both V4 models have a 1M-token context window, up to 384K output tokens, and support function calling, JSON mode, and optional chain-of-thought thinking. They are non-thinking by default here for fast responses; enable reasoning with thinking: {type: "enabled"}.
deepseek-v4-flash (default)
Best for: General conversations, coding, content generation, agent loops
deepseek-chat and deepseek-reasoner are deprecated. They are still accepted and resolve to deepseek-v4-flash (chat = non-thinking, reasoner = thinking), so existing configs keep working, but they will be removed in the next major release. The DeepSeek API itself retired those two names on 2026-07-24; this server keeps translating them to V4 for you in the meantime. New setups should use deepseek-v4-flash or deepseek-v4-pro directly.
Configuration
The server is configured via environment variables. All settings except DEEPSEEK_API_KEY are optional.
Variable
Default
Description
DEEPSEEK_API_KEY
(required)
Your DeepSeek API key
DEEPSEEK_BASE_URL
https://api.deepseek.com
Custom API endpoint
DEFAULT_MODEL
deepseek-v4-flash
Default model for requests
SHOW_COST_INFO
true
Show cost info in responses
REQUEST_TIMEOUT
60000
Request timeout in milliseconds
MAX_RETRIES
2
Maximum retry count for failed requests
SKIP_CONNECTION_TEST
false
Skip startup API connection test
MAX_MESSAGE_LENGTH
100000
Maximum message content length (characters)
SESSION_TTL_MINUTES
30
Session time-to-live in minutes
MAX_SESSIONS
100
Maximum number of concurrent sessions
FALLBACK_ENABLED
true
Enable automatic model fallback on errors
CIRCUIT_BREAKER_THRESHOLD
5
Consecutive failures before circuit opens
CIRCUIT_BREAKER_RESET_TIMEOUT
30000
Milliseconds before circuit half-opens
MAX_SESSION_MESSAGES
200
Max messages per session (sliding window)
RESPONSE_SCHEMA_MAX_RETRIES
2
Repair retries when a response_schema validation fails (0 disables)
ENABLE_MULTIMODAL
false
Enable multimodal (image) input support
TRANSPORT
stdio
Transport mode: stdio or http
HTTP_PORT
3000
HTTP server port (when TRANSPORT=http)
HTTP_HOST
127.0.0.1
Bind address for HTTP transport. Loopback by default so a fresh run is not exposed. Set to 0.0.0.0 to accept remote connections (do this only with auth or a proxy in front)
HTTP_AUTH_TOKEN
(unset)
When set, POST /mcp requires Authorization: Bearer <token>. /health stays open. Strongly recommended whenever the port is reachable beyond localhost
HTTP_ALLOWED_HOSTS
(unset)
Comma-separated list of allowed Host headers for DNS rebinding protection when binding to 0.0.0.0 (e.g. mcp.example.com,localhost). The Docker image defaults it to localhost,127.0.0.1,[::1]
HTTP_ALLOW_UNPROTECTED_BIND
false
Set to true to bind 0.0.0.0 with neither HTTP_ALLOWED_HOSTS nor HTTP_AUTH_TOKEN. The server otherwise refuses to start in that combination
Example with custom config:
claude mcp add -s user deepseek npx @arikusi/deepseek-mcp-server \
-e DEEPSEEK_API_KEY=your-key \
-e SHOW_COST_INFO=false \
-e REQUEST_TIMEOUT=30000
# Run all tests
npm test
# Watch mode
npm run test:watch
# With coverage report
npm run test:coverage
Testing Locally
# Set API key
export DEEPSEEK_API_KEY="your-key"
# Run the server
npm start
The server will start and wait for MCP client connections via stdio.
Remote Endpoint (Hosted)
A hosted BYOK (Bring Your Own Key) endpoint is available at:
https://deepseek-mcp.tahirl.com/mcp
Send your DeepSeek API key as Authorization: Bearer <key>. No server-side API key stored — your key is used directly per request. Powered by Cloudflare Workers (global edge, zero cold start).
Note: Thinking mode may take over 30 seconds for complex queries. Some MCP clients (e.g. Claude Code) have built-in tool call timeouts that may interrupt long-running requests. When latency matters, the default non-thinking mode is recommended.
# Test health
curl https://deepseek-mcp.tahirl.com/health
# Test MCP (requires auth)
curl -X POST https://deepseek-mcp.tahirl.com/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_KEY" \
-d '{"jsonrpc":"2.0","method":"initialize","params":{"capabilities":{}},"id":1}'
The MCP endpoint is available at POST /mcp (Streamable HTTP protocol).
Securing the endpoint (read before exposing it). In self-hosted HTTP mode the
server holds your DEEPSEEK_API_KEY and uses it for every deepseek_chat call.
Anyone who can reach POST /mcp can invoke tools and spend that key, so the
endpoint must not sit open on a public interface. The defaults are built around
this:
HTTP_HOST defaults to 127.0.0.1, so a plain run only listens on loopback and the SDK's DNS rebinding protection is active. Nothing off the machine can reach it.
To accept remote connections, set HTTP_HOST=0.0.0.0and either HTTP_AUTH_TOKEN (so /mcp requires Authorization: Bearer <token>) or HTTP_ALLOWED_HOSTS. Binding 0.0.0.0 with neither turns the SDK's Host-header check off entirely, which leaves /mcp open to DNS rebinding from any web page you visit, so the server refuses to start rather than warn. HTTP_ALLOW_UNPROTECTED_BIND=true overrides the refusal if you really want an open endpoint.
For an internet-facing deployment, put an authenticating reverse proxy with TLS in front and set HTTP_ALLOWED_HOSTS to your real hostname(s).
HTTP_AUTH_TOKEN is a static gateway token for the self-hosted endpoint and is
unrelated to your DeepSeek key. It is separate from the hosted BYOK endpoint
above, where clients pass their own DeepSeek key as the bearer.
Session isolation (1.7.0+): In HTTP transport each connected MCP session
gets its own McpServer instance and its own SessionStore. Conversation
history, session listings, and deletions are scoped to the MCP session that
created them, so one client cannot read, enumerate, or wipe another client's
sessions. STDIO transport is single-tenant by nature and unaffected.
Docker
# Build
docker build -t deepseek-mcp-server .
# Run, reachable only from the host's loopback, with a bearer token
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
# Or use docker-compose
DEEPSEEK_API_KEY=your-key HTTP_AUTH_TOKEN=your-token docker compose up -d
The image runs HTTP transport on port 3000 with a health check. Inside the
container it binds 0.0.0.0 (required for the port mapping to work), so control
exposure at the publish layer: the example above and the bundled
docker-compose.yml publish to 127.0.0.1 only.
Publishing on loopback is not on its own enough, because DNS rebinding targets
the loopback address your own browser can already reach. The image therefore
ships HTTP_ALLOWED_HOSTS=localhost,127.0.0.1,[::1], which keeps the Host
check installed. Publishing under a real hostname? Add it to that list, or every
request carrying it gets a 403. Publishing on a public interface? Set
HTTP_AUTH_TOKEN as well.
Troubleshooting
"DEEPSEEK_API_KEY environment variable is not set"
Option 1: Use the correct installation command
# Make sure to include -e flag with your API key
claude mcp add deepseek npx @arikusi/deepseek-mcp-server -e DEEPSEEK_API_KEY=your-key-here
Option 2: Manually edit the config file
If you already installed without the API key, edit your config file:
For Claude Code: Open ~/.claude.json (Windows: C:\Users\USERNAME\.claude.json)
Find the "mcpServers" section under your project path
Found a bug or have a feature request? Please open an issue using our templates.
Development
# Clone the repo
git clone https://github.com/arikusi/deepseek-mcp-server.git
cd deepseek-mcp-server
# Install dependencies
npm install
# Build in watch mode
npm run watch
# Run tests
npm test
# Lint
npm run lint