@deepseek-ai/dsh-mcp-client
English | 中文
MCP client bridge plugin: connects to external Model Context Protocol servers and registers their tools on ctx.tools, making them available to the model as native tools under server-qualified names (mcp__<serverName>__<rawName>).
Usage
One plugin instance per MCP server in cordis.yml:
- 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}`'
The model sees mcp__github__create_issue, mcp__web__search, … — the same server-qualified shape Claude Code and Codex use. HMR hot-swaps: editing the entry triggers disconnect + reconnect without process restart; an unchanged serverName reproduces identical tool names.
Config
| Field | Transport | Required | Description |
|---|---|---|---|
transport |
both | yes | "stdio" or "streamable-http" |
serverName |
both | yes | Namespace for this server's model-facing tool names; [A-Za-z0-9_-]{1,32}, unique across live instances |
command |
stdio | yes | Executable to spawn |
args |
stdio | no | Arguments passed to the command |
env |
stdio | no | Extra env vars merged on top of scrubbed ambient env |
cwd |
stdio | no | Working directory for the child process |
url |
http | yes | MCP server URL |
headers |
http | no | Extra headers (e.g. auth tokens) |
toolCallTimeoutMs |
both | no | Timeout per callTool invocation (default 60000) |
failOnStartupError |
both | no | Reject plugin activation when initial connection or tool synchronization fails (default false) |
Tool naming
Every MCP tool has two names: the raw MCP name (sent on the wire in tools/call) and the public name mcp__<serverName>__<rawName> registered on ctx.tools. Public names are normalized to the DeepSeek function-name contract (64 chars, [A-Za-z0-9_-]); when replacement or truncation changes the name, a deterministic 12-hex-char hash of (serverName, rawName) is appended so distinct tools never collapse into one name. Names are pure functions of (serverName, rawName) — connection order, re-syncs, and other servers never rename a tool.
- Two servers publishing the same raw name (e.g.
search) coexist under their namespaces. - A duplicate
serverNameacross live instances fails the later plugin instance at load. - A server listing the same tool name twice is rejected as an invalid tool list.
- A foreign registration squatting on this server's namespace rolls back the whole generation (never a partial set), with a loud error.
Behavior
- On connect: plugin activation awaits
listTools()and registers each tool viactx.tools.register()under its public name before the composition starts its first turn. Initial connection, discovery, or registration failure is always logged; it rejects activation whenfailOnStartupErroris true and otherwise activates with no tools. - Listens for
notifications/tools/list_changed→ re-syncs; a fetch-phase failure keeps the previous generation registered, while a registration conflict rolls back the attempted generation and leaves no tools from that server. - Tool execute:
client.callTool({ name: rawName, arguments }, { signal })with timeout + abort support—the public name is never sent to the server. - Canonical success is
{ content: JsonValue[], structuredContent? }; complete JSON MCP blocks survive for programmatic callers. A supported advertisedoutputSchemavalidatesstructuredContent; unsupported schema vocabulary falls back to unconstrainedJsonValue. - Native/model rendering keeps the existing text projection: text blocks join with newlines while image, audio, resource, and unsupported blocks become placeholders.
- On disconnect/crash: no auto-reconnect. Registered tools remain until plugin disposal or a successful re-sync, and calls can fail against the closed transport; reload with HMR or restart the Host to reconnect.
Services consumed
| Service | Usage |
|---|---|
ctx.tools |
Register/unregister MCP tools |
Model Experience
Discovered MCP tools
What the model sees
After initial discovery succeeds, each advertised MCP tool appears as a native tool named mcp__<serverName>__<rawName> (or its deterministic normalized form), with the server-provided description and input schema. A successful re-sync replaces the generation; plugin disposal removes it.
Token effect
Data-dependent schema cost is paid on every request while the tools are registered. Re-sync replaces rather than accumulates schemas, and the server-qualified name adds tokens to every tool definition and call.
KV Cache effect
Prefix-stable while the discovered tool set and schemas are unchanged. A re-sync that adds, removes, renames, or changes a tool replaces definitions and may invalidate reuse from the first changed schema token.
Tool-call history and results
What the model sees
The public tool name and JSON arguments remain in assistant history. Text result blocks are joined with newlines into one retained Native text result; image, audio, resource, and unsupported blocks become short placeholders there. Their full JSON blocks and optional structured content remain in the execution-local canonical value, and MCP isError rejects the call through the registry's error path.
Token effect
Arguments and mapped text are retained until compaction. Binary and resource payloads are discarded rather than added to context.
KV Cache effect
Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
Known Limitations and Deferred Work
- Tools are the only bridged MCP capability — Resources and Prompts have no harness consumption surface and are deferred.
- Startup timeout is inherited from the MCP SDK — DSH does not yet expose a connection/discovery timeout. Each initialize or paginated
tools/listrequest uses the SDK's 60-second default, so an unresponsive server or cursor chain can delay both activation and teardown while the initial synchronization settles. - Crash recovery is manual — transport closure does not auto-reconnect; registered tools can remain visible but fail against the closed transport until an HMR reload or Host restart.
- Native non-text rendering is lossy — image, audio, and resource payloads become placeholders in model context even though the execution-local canonical value preserves their JSON blocks. Richer Native multimedia projection is deferred.
- Unsupported MCP output schemas are not enforced —
structuredContentfalls back toJsonValuewhen the advertised schema uses vocabulary outside the harness subset.