deepseek-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@deepseek-mcpRefactor the auth module and run the tests."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
deepseek-mcp
An MCP server that lets Claude Code delegate one bounded unit of repository work to DeepSeek as a local sub-agent.
Claude stays the orchestrator: it decides scope, architecture, and correctness. DeepSeek is an execution worker for the token-heavy part — exploring the repository, making routine or repetitive changes, and running the tests — inside a single authorized workspace and under hard budgets.
The point is to stop paying twice for the same context. If Claude reads a subsystem and then DeepSeek reads it again, nothing was saved; so the delegation decision belongs before the broad reads.
User
↓
Claude: plan + define goal/scope
↓
DeepSeek: inspect repo + read code + implement + test
↓
DeepSeek: compact structured summary
↓
Claude: review diff/results + final answerDeepSeek is the primary repository worker; Claude is the orchestrator. Claude plans, makes the architectural and security calls, reviews the returned diff, and writes the final answer. DeepSeek does the repository work: exploring, Glob/Grep/ Read, understanding the code, implementing, testing, and routine fixes. Claude delegates before broadly reading source files, and DeepSeek discovers the relevant files itself inside the authorized scope and returns a compact structured summary — Claude never sends file contents.
Requires Python 3.11+ and a DeepSeek API key. One runtime dependency: the MCP
SDK. Everything else is the standard library. ripgrep is used for search when
present and a pure-Python scan is used when it is not.
1. Install
The package is not published to PyPI yet, so install it from a checkout. Install it once, globally — it is not a project dependency, and it works across every repository.
git clone https://github.com/giaminhgist/DeepSeek_MCP.git
cd DeepSeek_MCP
uv tool install . # recommended: isolated, and puts deepseek-mcp on PATH
# or
pipx install .
# or, into the current environment
pip install .Confirm the console script resolves:
deepseek-mcp --version # -> deepseek-mcp 0.1.0If the command is not found, the install directory is not on your PATH. With
uv, run uv tool update-shell and open a new shell.
deepseek-mcp with no arguments starts the MCP server on stdio. That is what
Claude Code runs; you would not normally invoke it yourself.
Related MCP server: claude-code
2. Set the API key
Get a key from https://platform.deepseek.com/. Never put it in a project repository. Export it from your shell profile:
export DEEPSEEK_API_KEY="sk-your-key-here" # ~/.bashrc, ~/.zshrc, …# Windows PowerShell
setx DEEPSEEK_API_KEY "sk-your-key-here"Then check the server can see it:
deepseek-mcp --check # prints a health report as JSON; exits 1 if unusable--check prints "mode": "enabled" and "status": "ok" when the key is
readable. The key itself never appears in the report.
Three supported key sources, in precedence order:
DEEPSEEK_MCP_API_KEYorDEEPSEEK_API_KEYin the server environment.api_key_envin the user config file, naming a different environment variable to read.api_keyin the user config file — accepted, discouraged, and it produces a startup warning, because it puts the key on disk.
Everything else is optional; see Configuration reference.
The only other variable worth knowing up front is DEEPSEEK_MCP_WORKSPACE, which
pins the authorized project root instead of discovering it (see
Workspace).
3. Add the server to Claude Code
If DEEPSEEK_API_KEY is already exported in the environment Claude Code
inherits:
claude mcp add deepseek --scope user -- deepseek-mcpIf it is not — for example a desktop launch that does not read your shell profile — pass it explicitly:
claude mcp add deepseek --scope user -e DEEPSEEK_API_KEY=sk-your-key-here -- deepseek-mcp--scope user registers it for every project. Use --scope local for the
current project only.
Equivalent hand-written configuration:
{
"mcpServers": {
"deepseek": {
"command": "deepseek-mcp",
"env": {
"DEEPSEEK_API_KEY": "sk-your-key-here"
}
}
}
}Omit the env block entirely when the key is already in the inherited
environment. Do not commit a key into any repository file.
4. Verify the connection
claude mcp list # deepseek should be listed and connectedThen, inside Claude Code:
run
/mcp—deepseekshould appear with its two tools;ask Claude to call
deepseek_health. A working server answers withstatus: "ok",mode: "enabled", the defaultmodeland theallowed_modelsallowlist, the resolved workspace root, the enabled capabilities, the budget limits, and ausageobject with the worker's running totals for this server process.
With no key configured, the server still starts and still answers
deepseek_health — reporting status: "error", mode: "disabled" — so the
problem is diagnosable from inside Claude Code. It performs no work in that
state.
5. Troubleshooting
Symptom | Cause and fix |
| The install directory is not on |
| Run |
| No API key reached the server process. Check the |
Delegation returns | Policy refused the request before any API call: a mode asking for capabilities the server does not grant, verification commands in |
Delegation returns | The unit of work was too large for the limits |
A | The executable policy is an allowlist. See Command policy; add project-specific tools through |
The worker cannot read a file | Secret-bearing paths and |
The workspace root is wrong | It is discovered by walking up from the directory Claude Code launched the server in. Set |
| An explicitly pointed-at config file is missing. Fix the path or unset the variable; the server will not silently fall back to defaults. |
Logs go to stderr as event key=value records, never to stdout. Raise the detail
with DEEPSEEK_MCP_LOG_LEVEL=DEBUG, or send them to a file with
DEEPSEEK_MCP_LOG_FILE=/absolute/path.log.
The tool surface
Two tools, deliberately.
deepseek_health
Configuration and health: status, the default model and the allowed_models
allowlist, the authorized workspace root and how it was resolved, enabled
capabilities, budget limits, and a usage object with the worker's running
totals for this server process. No secrets. Use it to confirm the worker is
usable and to size a delegation before sending one.
delegate_to_deepseek
One bounded unit of work, as a structured contract rather than a prose blob:
Field | Purpose |
| The outcome required. Required. |
| Workspace-relative globs the work belongs to. Restricts writes. |
| Only the project rules that matter for this task. |
| Conditions that define success. |
| Commands to run before finishing, as argv arrays. |
|
|
| DeepSeek model for this delegation only, e.g. |
| Also return a repository map — |
{
"objective": "Treat a None row as invalid and cover it with a test.",
"scope": ["src/importer/**", "tests/importer/**"],
"constraints": ["Do not change the public response schema."],
"acceptance_criteria": ["validate_row(None) returns False."],
"verification": [["pytest", "tests/importer", "-q"]],
"mode": "write"
}The worker then loops on its own — glob, grep, read, edit, run, repair — and returns a compact result, never a transcript:
{
"status": "completed",
"summary": "Treated a None row as invalid and added a regression test.",
"changed_files": ["src/importer/validate.py"],
"created_files": ["tests/importer/test_none.py"],
"deleted_files": [],
"inspected_files": ["src/importer/__init__.py"],
"verification": [
{"argv": ["pytest", "tests/importer", "-q"], "exit_code": 0, "summary": "24 passed"}
],
"warnings": [],
"unresolved": [],
"assumptions": [],
"diff_stat": " src/importer/validate.py | 3 ++-",
"metrics": {
"turns": 7, "tool_calls": 12, "prompt_tokens": 18400,
"completion_tokens": 2100, "duration_seconds": 41.2,
"files_read": 4, "files_changed": 2, "compactions": 0
},
"session_usage": {
"delegations": 3, "turns": 19, "tool_calls": 31,
"prompt_tokens": 51200, "completion_tokens": 6400,
"total_tokens": 57600, "since": "server start"
},
"model": "deepseek-chat",
"analysis": {
"important_files": ["src/importer/validate.py"],
"architecture_notes": ["validate_row is the single entry point for row checks."],
"dependencies": ["src/importer/schema.py"],
"suggested_scope": ["src/importer/**", "tests/importer/**"],
"risks": ["Changing the None handling may affect callers that rely on the old behaviour."]
},
"debug_ledger": ["R src/importer/validate.py", "E src/importer/validate.py", "X pytest tests/importer -q"]
}model is the model that actually ran the delegation, after per-task override
resolution. analysis is present only when the delegation asked for it, and
carries the five repository-map fields. debug_ledger is the compact
one-line-per-tool-call execution ledger, populated only when the server runs with
debug enabled. metrics reports this delegation's own token usage and shape;
session_usage carries the worker's running totals for this server process,
including this delegation — delegations, turns, tool_calls,
prompt_tokens, completion_tokens, total_tokens, and since (always the
literal "server start").
The counter behind usage and session_usage is in-memory and scoped to the
server process: it resets when the MCP server restarts, which is exactly what the
since field records. It is not persisted to disk. Only delegations that reached
the worker are counted — a request refused earlier (server disabled, an invalid
request field, or a model outside the allowed_models allowlist) never called a
model, so it does not increment delegations or any token count. total_tokens
is computed from its parts rather than stored, so it cannot drift.
Statuses: completed, partial, blocked, failed, budget_exceeded,
disabled. Expected failures — bad configuration, a refused path, a denied
command, an exhausted budget, a provider error — all come back as one of these
with a reason. A Python traceback never does.
Results carry conclusions, never raw Read/Grep/tool transcripts. The debug ledger is the compact ledger — one line per tool call — not tool output, even when debug is enabled.
Modes narrow, never widen
Mode | Read & search | Run commands | Write files |
| yes | no | no |
| yes | yes | no |
| yes | yes | yes, inside |
A mode is intersected with the server's configured capabilities. A request asking for more than the server grants is refused before any API call — it can never widen policy.
What the worker is not trusted with
Two things in the result do not come from the model:
The changed-file list comes from watching the tools plus a
git statuscomparison against a snapshot taken before the run, so a user's pre-existing uncommitted edits are never reported as the worker's work.The status. A claimed
completedis downgraded topartialif requested verification never ran or exited non-zero.failedandbudget_exceededare the server's verdicts and cannot be claimed by the worker at all.
Verify anyway. git status --short, git diff --stat, then read the changed
hunks in proportion to risk. completed is a claim, not proof.
Budgets
Every delegation is bounded, and the run stops with a structured reason rather
than overrunning: turns (24), tool calls (80), wall clock (15 min), output per
tool call (20,000 chars), Read window (250 lines), Grep matches (100), Glob
paths (300), and estimated active context (96,000 tokens).
Context is treated as a budgeted resource rather than a growing transcript. Above a threshold, old tool payloads are replaced by one-line entries from a deterministic execution ledger; if that is not enough, whole old turns are dropped, since the ledger still records what they did. The system prompt, the original task contract, the recent turns, and the ledger are always preserved. No extra model call is ever spent summarizing, and if the worker needs an evicted detail it reads the file again.
All limits are configurable and reported by deepseek_health.
Configuration reference
Settings marked reserved are validated at startup but not yet consumed.
Precedence
environment variable > user config file > built-in default
Model selection has one more level above that: a single delegation may name its own model, so the full order is
per-task model > environment variable > user config file > built-in default
The per-task model is the model parameter on delegate_to_deepseek. When the
server's allowed_models is non-empty it is an allowlist, and a request naming
anything else is refused as blocked before any API call — model choice narrows
to what the operator permitted, exactly like delegation modes narrow to the
server's capabilities.
A missing, malformed, or contradictory setting is an error. The server does not fall back to a broader workspace or a more permissive policy.
If configuration fails to load, the process still starts and still answers
deepseek_health, but reports status: "error", mode: "disabled", and does no
work. Run deepseek-mcp --check to see the same report on the command line.
Config file location
The user config file lives outside any project:
Platform | Path |
Linux/BSD |
|
macOS |
|
Windows |
|
DEEPSEEK_MCP_CONFIG overrides the path. If it is set and the file does not
exist, startup fails rather than silently using defaults. An absent config file
at the default location is fine; an empty file is fine; unknown keys are an
error.
Config file schema
Every key is optional.
{
"model": "deepseek-chat",
"allowed_models": ["deepseek-chat", "deepseek-reasoner"],
"base_url": "https://api.deepseek.com/v1",
"api_key_env": "DEEPSEEK_API_KEY",
"workspace": "/absolute/path/to/project",
"tools": {
"enabled": ["Read", "Glob", "Grep", "Edit", "Write", "Run"],
"max_write_bytes": 2000000,
"allow_secret_paths": false,
"secret_path_exceptions": []
},
"provider": {
"timeout_seconds": 120,
"max_retries": 3,
"retry_base_delay": 0.5,
"retry_max_delay": 8.0,
"temperature": 0.0,
"max_output_tokens": 4096
},
"budgets": {
"max_turns": 24,
"max_tool_calls": 80,
"max_wall_seconds": 900,
"max_tool_output_chars": 20000,
"read_window_lines": 250,
"max_grep_matches": 100,
"max_glob_paths": 300,
"max_context_tokens": 96000,
"compaction_threshold_ratio": 0.7
},
"commands": {
"default_timeout_seconds": 120,
"max_timeout_seconds": 600,
"extra_denied_executables": [],
"extra_allowed_executables": [],
"allow_unsafe_shell": false
},
"logging": { "level": "INFO", "file": null, "log_task_text": false },
"debug": false
}An api_key key is accepted here but discouraged: it puts the key on disk and
produces a startup warning. Prefer api_key_env, which names the environment
variable to read instead.
allowed_models is an optional allowlist of model names a delegation may request.
When non-empty, the configured model must be in it (the settings contradict
each other otherwise), and a per-task model naming anything outside it is
refused as blocked before any API call. Empty means any well-formed model name
is accepted.
Credentials and endpoint
Variable | Effect |
| API key, highest precedence |
| API key (default variable name; override with |
| Model name. |
| Comma-separated allowlist of model names a delegation may request. Empty means any well-formed name. A per-task |
| OpenAI-compatible base URL. Must be |
| Config file path |
No API key means no work: startup reports the server as disabled.
Workspace
Variable | Effect |
| Absolute path to the authorized project root |
With no explicit workspace, the root is discovered by walking up from the
process working directory looking for .git, .hg, .svn, pyproject.toml,
package.json, go.mod, or Cargo.toml. If none is found, the working
directory itself is used and a warning is recorded.
An explicit workspace that is missing, unreadable, not a directory, relative, or the filesystem root is a startup error. It never degrades to the working directory. The server does not need to live inside your project, and it never modifies a project to activate itself.
Tools
Variable | Effect |
| Comma-separated list from |
| Write size ceiling, and the largest existing file the worker will overwrite |
| Off by default: |
Default enabled tools are everything except NotebookEdit. NotebookEdit is a
recognised name with no implementation yet, so enabling it is a startup error
rather than a tool the worker is offered and cannot use. Read is mandatory.
.git, .hg and .svn internals are never readable or writable through the
filesystem tools; use a read-only git command through Run instead.
Budget limits
All enforced per delegation, and reported by deepseek_health so Claude can size
a delegation before sending it.
Variable | Default | Effect |
| 24 | Provider calls per delegation |
| 80 | Tool executions per delegation |
| 900 | Total wall clock; also clamps command timeouts |
| 20000 | Per tool result, keeping head and tail |
| 250 | Lines per |
| 100 | Matches per |
| 300 | Paths per |
| 96000 | Hard ceiling on estimated active context |
| 0.7 | Fraction of the ceiling that triggers compaction |
Exceeding a budget ends the delegation with status: "budget_exceeded" and a
reason, after reporting whatever work already landed.
Provider
Variable | Default | Effect |
| 120 | Per-request timeout, clamped to the remaining wall budget |
| 3 | Retries after the first attempt, transient failures only |
| 0.5 | Exponential backoff base, with jitter |
| 8.0 | Backoff cap |
| 0.0 | Sampling temperature |
| 4096 | Per-turn completion cap |
Timeouts, connection failures, 429 and 5xx are retried. A 4xx surfaces
immediately, because retrying a bad key or a bad request only wastes time.
Redirects are refused outright so the Authorization header cannot be replayed
to another host.
Command policy
Variable | Default | Effect |
| 120 | Default per-command timeout |
| 600 | Ceiling the worker cannot raise |
| false | Reserved. Raw shell execution is not implemented; this setting grants nothing |
Run executes an argv array with shell=False. The executable must be on an
allowlist, and dangerous subcommands are refused structurally. Use the config
file's commands.extra_allowed_executables to add a project-specific tool, and
extra_denied_executables to remove one. An extra allow entry cannot re-enable a
hard-denied program.
Refused by default: privilege escalation, package installation, publishing,
network utilities, shells and inline-code interpreters, destructive filesystem
operations, in-place editors, and mutating or remote git subcommands. Read-only
git (status, diff, log, show, ls-files, rev-parse, blame, …) is
allowed.
General-purpose file readers such as cat, head and grep are deliberately
not allowlisted: they would be a one-command bypass of the secret-path deny
list that Read, Glob and Grep enforce. Add one back through
extra_allowed_executables only if you accept that.
Logging
Variable | Effect |
|
|
| Absolute path. Created with |
| Reserved. Opt-in task-text logging. Off by default and not yet consumed |
| Reserved. Debug detail in results. Not yet consumed |
Delegation logging is metadata only: event name, status, mode, turn and tool-call counts, token counts, duration, and file counts. No task text, file contents, command output, or prompt bodies. Logs go to stderr, never stdout — stdout carries MCP protocol traffic only. The API key is scrubbed from every record as a backstop.
Security posture
Read this section before deciding what to point the worker at.
The protections described here are unchanged by the model-selection and analysis features: bounded context with compaction, the workspace sandbox, the command allowlist, no commit or push, no package installation or network access by default, and a structured result carrying token and tool metrics.
What is enforced, in code:
Every path resolves against one workspace root. Symlinks are followed first and the result is what gets validated, so a link out of the tree is refused. Write targets have their parent directory revalidated immediately before the write.
An explicitly configured workspace that is missing or unusable is a startup error. It never silently falls back to a broader directory.
Secret-bearing paths (
.env,.env.*,*.pem,*.key,id_rsa,.netrc,.ssh/,.aws/, and similar) and.git/.hg/.svninternals are denied to every tool, and are omitted from search results rather than merely unreadable.A delegation's
scoperestricts writes. Reads stay open across the workspace, because the worker has to explore to do its job.Runusesshell=False. There is no shell, so&&,|,$(...)and>arrive as literal argument text and cannot chain a second command. The executable must be on an allowlist, dangerous subcommands are refused structurally over argv, absolute path arguments must land inside the workspace, and an argument naming an existing denied path is refused.Package installation, publishing, network utilities, privilege escalation, and mutating or remote git subcommands are refused by default. So are general-purpose file readers such as
catandgrep, which would otherwise be a one-command bypass of the secret-path deny list.Subprocesses receive an environment with credentials stripped, so the worker's own API key cannot surface in command output or a log.
Writes are atomic (temp file, fsync, rename), so an interrupted write leaves the original file intact.
Editcan require the SHA-256 thatReadreturned, so a stale edit is refused rather than applied.The system prompt states that repository content is data, not instruction — and the limits above are enforced server-side, so a file that tells the worker to ignore its instructions cannot grant it anything.
Logs are metadata only by default: event, status, counts, durations. No task text, file contents, command output, or prompt bodies. The API key is scrubbed from every record as a backstop.
What this is not: OS-level adversarial sandboxing.
This is application-level policy. It bounds the category of action a confused, mistaken, or prompt-injected worker can take. It is not a containment boundary against a determined adversary, and the two are not equivalent.
Concretely:
An allowed test runner executes your project's code.
pytestimports the repository;make testruns whatever the Makefile says. Anything reachable that way is reachable, including files the path policy would have refused.There is no process, filesystem, or network isolation — no container, no bubblewrap or seccomp, no macOS sandbox profile, no Windows job object, no network namespace. A command that is permitted runs with the same privileges as the server process.
The deny lists are structural rather than exhaustive. They are the reason the executable policy is an allowlist: unknown programs are refused instead of assumed safe.
Do not point this at a repository you would not run tests from, and do not treat it as a substitute for reviewing the diff.
Known limitations
NotebookEditis a recognised tool name with no implementation. Enabling it is a startup error rather than a tool the worker is offered and cannot use.commands.allow_unsafe_shellis validated but does nothing; there is no raw shell execution.The worker cannot delete files. There is no delete tool, and
rmis refused.No OS-level sandbox, as above.
The context estimate is a character heuristic, calibrated upward by the provider's reported usage. It is deliberately conservative, not exact.
Search behaviour differs slightly between the
ripgrepand pure-Python engines, because the regex dialects differ. The engine used is named in every result.Windows is supported and tested in CI, but process-group termination on timeout is best-effort there compared to POSIX.
One delegation at a time per call. There are no background jobs, no persistent worker memory, and no automatic git commit or push.
Development
uv venv && uv pip install -e ".[dev]"
python -m pytest # the full suite; no API key and no network needed
python -m ruff check .
python -m ruff format --check .
python -m mypyThe test suite never calls a paid API: a scripted fake provider stands in, and the MCP integration tests drive a real server subprocess over stdio against temporary git repositories.
phases/ holds the implementation sequence this server was built from, kept for
reference.
GLOBAL_CLAUDE.md is not part of this codebase. It is a user-level Claude Code
instruction file describing when to delegate — copy it to ~/.claude/CLAUDE.md,
or merge it into the one you already have.
License
MIT.
This server cannot be installed
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityDmaintenanceRun DeepSeek as a real sub-agent inside Claude Code / Codex CLI — not just a single LLM call. DeepSeek gets its own 7-tool agent loop (Read/Write/Edit/Bash/Glob/Grep/NotebookEdit) inside a sandboxed workspace.223MIT
- AlicenseNot gradedqualityBmaintenanceEnables Codex to delegate tasks to Claude Code, allowing Claude to investigate, edit, and verify changes in the repository with background job management.2MIT
- AlicenseAqualityCmaintenanceBridges a main agent (e.g., Codex) to a separate execution model in Claude Code Haha Desktop, enabling delegated coding tasks with file modifications, test runs, and change auditing.6MIT
- AlicenseNot gradedqualityBmaintenanceEnables Codex to delegate bounded engineering jobs to Claude Code CLI in isolated Git worktrees with strict security and allowance pacing.MIT
Related MCP Connectors
Cross-agent artifact workspace with provenance across Claude Code, Codex, Cursor, LangGraph.
Deterministic AI code review, with an audit record. Governance inside the agent loop.
Coding agents from Claude Code, Cursor and Codex claim jobs and lock files on one shared board.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/giaminhgist/DeepSeek_MCP'
If you have feedback or need assistance with the MCP directory API, please join our Discord server