How to Use MCP Servers with DeepSeek-Harness (Complete Guide)
Configure MCP servers in DeepSeek-Harness with dsh-mcp-client — transport options, tool naming rules, reconnection behavior, and the official memory example.
DeepSeek-Harness (dsh) supports the Model Context Protocol (MCP) as a client, through the official @deepseek-ai/dsh-mcp-client plugin. You add one plugin instance per MCP server in a cordis.patch.yml (or any --patch file), and dsh exposes that server's tools to the agent under the name mcp__<serverName>__<rawName>. This guide covers the exact config shape, how tool names get built, and the reconnection and startup behavior you need to know before you rely on it.
MCP support is a first-class, documented feature — with one big caveat
MCP in dsh isn't experimental or half-finished. The dsh-mcp-client package ships full docs, supports both major MCP transports, and has a defined reconnection policy. The caveat: dsh only bridges MCP Tools. MCP's Resources and Prompts capabilities have no consumer in the harness and are explicitly listed as deferred in the docs. If an MCP server you're evaluating leans heavily on Resources or Prompts rather than Tools, plan around that gap — the tools will show up, the rest won't.
Configuring an MCP server
Each server is a separate @deepseek-ai/dsh-mcp-client plugin entry in your patch file. Two transports are supported: stdio (spawn a local process) and streamable-http (connect to a running HTTP endpoint).
- id: mcp-github
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: github
transport: stdio
command: npx
args: ['-y', '@modelcontextprotocol/server-github']
env:
GITHUB_TOKEN: !!js process.env.GITHUB_TOKEN
- id: mcp-web
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: web
transport: streamable-http
url: http://localhost:3000/mcp
headers:
Authorization: !!js '`Bearer ${process.env.MCP_TOKEN}`'
For stdio, you provide command/args/env/cwd — dsh spawns and manages the subprocess. For streamable-http, you provide url/headers and dsh connects over HTTP. Editing this config line while dsh is running triggers hot-module reload: the server disconnects and reconnects, but the dsh process itself doesn't restart. Keep serverName stable across edits — changing it changes every tool name the model sees for that server.
If you're patching a web or headless profile, drop this block into the profile's own cordis.patch.yml, or layer it in with --patch <path> at launch time. See our configuration guide for how patch layers stack.
How tool names get built
The model never sees serverName and rawName as separate fields — it sees one composed tool name:
mcp__<serverName>__<rawName>
This is deliberately the same "server-qualified" naming shape used by Claude Code and Codex, which matters if you're used to reading tool-call traces from either of those. A few normalization rules apply, since the composed name has to fit DeepSeek's function-name constraints:
| Rule | Detail |
|---|---|
| Character set | Restricted to [A-Za-z0-9_-], 64 characters max |
| Collisions | If two servers/tools would normalize to the same name, dsh appends a 12-character hex suffix derived from (serverName, rawName) |
| Stability | Names are pure functions of (serverName, rawName) — they don't depend on connection order, so a tool's name is stable across restarts |
Startup and reconnection behavior
Two settings control what happens when a server is slow, unreachable, or drops mid-session:
| Setting | Default | Effect |
|---|---|---|
failOnStartupError | false | If the initial connection or tool discovery fails, the plugin degrades to zero tools instead of blocking the rest of the profile from starting |
reconnect.enabled | true | Automatic reconnection on drop, with exponential backoff |
| Initial reconnect delay | 500ms | First retry after a disconnect |
maxDelayMs | 30000ms | Backoff ceiling; a connection that stays up longer than this resets the failure-count budget |
maxAttempts | 10 | Consecutive failures before giving up, until an HMR reload or host restart |
Startup timeout is inherited from the MCP SDK's default of 60 seconds — dsh doesn't currently expose its own connect/discovery timeout as a config field, so a server that takes longer than that to respond during initial discovery will time out regardless of what you set elsewhere.
One more behavior worth knowing: non-text content (images, audio, other binary resources) coming back from an MCP tool call is rendered as a placeholder in what the model sees in conversation history — it's "lossy" at the display layer. The underlying JSON result blocks and structuredContent are fully preserved during execution, so tools that consume that structured data downstream aren't affected; it's specifically the model-facing history rendering that drops the raw bytes.
Secrets in your MCP config
Notice the !!js process.env.GITHUB_TOKEN and !!js 'Bearer ${process.env.MCP_TOKEN}' lines in the examples above — that's Cordis's YAML tag for embedding a JavaScript expression evaluated at load time, which is how you pull credentials from the environment instead of hardcoding them into a patch file you might commit or share. Treat any cordis.patch.yml that references real credentials the same way you'd treat a .env file: keep it out of version control, and prefer environment-variable references over literal tokens whenever the field supports it. If you're layering MCP config through $DSH_HOME/cordis.patch.yml for machine-wide defaults, see our configuration guide for how that layer interacts with profile-local patches.
Try it with the official memory example
The dsh repository ships a working reference under examples/mcp-memory/, with three separate cordis configs wiring up different "memory-style" MCP servers: engram.cordis.yml, mcp-reference-memory.cordis.yml, and memorix.cordis.yml. It's the fastest way to see a real dsh-mcp-client config end to end, and it's directly relevant if you're trying to solve dsh's lack of a built-in long-term memory feature — memory is one of the more common reasons people reach for MCP in the first place. See our roundup of DeepSeek-Harness memory plugins for how these MCP-based approaches compare to plugin-native memory stores.
Community MCP plugins worth knowing about
Beyond hand-writing cordis.patch.yml entries, a few community plugins in the MCP & Connectors category build on top of dsh-mcp-client rather than replacing it:
- dsh-mcp-manager adds a Settings → MCP page with OAuth (PKCE and dynamic client registration) so you can manage servers without hand-editing YAML.
- dsh-mcp-panel is a read-only runtime panel showing connection status and registered tools per server — useful for debugging the reconnection behavior above.
- dsh-mcp-bridge ships a curated bundle of common servers (memory, filesystem, GitHub, Playwright) in one install.
- dsh-search-mcp swaps dsh's built-in web search for a search MCP server (Tavily/Brave/Exa/Perplexity/DuckDuckGo), which is a common first MCP integration people reach for.
FAQ
Does dsh support MCP Resources and Prompts?
No. As of August 2026, dsh-mcp-client only bridges the Tools capability of MCP servers. Resources and Prompts are explicitly documented as deferred — there's no harness-side consumer for them yet.
Do I need to restart dsh after adding an MCP server?
No, if you're editing a patch file that's already loaded — the plugin tree supports hot-module reload via @deepseek-ai/cordis-plugin-hmr, so an MCP config change disconnects and reconnects just that server without restarting the process.
What happens if an MCP server is down when dsh starts?
By default (failOnStartupError: false), dsh starts anyway with zero tools registered for that server, rather than failing the whole profile. Set failOnStartupError: true if you'd rather fail loudly.
Can I use MCP servers that require OAuth?
The base dsh-mcp-client config takes static headers or env vars for auth. For OAuth flows specifically, community plugins like dsh-mcp-manager and dsh-oauth-mcp-client add that on top — the official client itself doesn't document a built-in OAuth flow.
How is dsh's MCP tool naming different from Claude Code's?
It isn't, deliberately — both use the mcp__<server>__<tool> server-qualified shape, which is worth knowing if you're moving between the two harnesses regularly.
Next steps
Pair this with our memory plugins roundup if MCP is solving a memory problem for you, or read DeepSeek-Harness vs Claude Code for how the two harnesses' MCP support compares side by side. For the full catalog of connector plugins, browse MCP & Connectors and best DeepSeek-Harness MCP plugins.