dsh-mcp-lazy
面向 DSH 的懒加载 MCP 网关:用一个 schema 稳定的代理工具替代 N 个工具定义,服务器按需启动、空闲退出,元数据缓存到磁盘
wings1848
@wings1848
⬇ 1
★ 4
main
安装
dsh plugin --profile web add github:wings1848/dsh-mcp-lazy
需要可复现安装时,可在仓库后追加 #commit 固定提交。
面向 DSH 的懒加载 MCP 网关:用一个 schema 稳定的代理工具替代 N 个工具定义,服务器按需启动、空闲退出,元数据缓存到磁盘
该插件未提供要点说明,请参考仓库 README。
deepseekdshdsh-pluginllmmcpmodel-context-protocol
- 安装并启动 DeepSeek Harness:
npx @deepseek-ai/dsh web - 在终端执行上面的安装命令(CLI 会解析插件并核验来源)
- 用 dsh plugins list 确认已安装,必要时重启 Harness 生效
插件以当前 dsh 进程的权限运行,安装时可能执行代码。请先通读仓库源码与许可证,确认无破坏性命令与越权访问;本站只做索引,不对第三方插件安全性作担保。
| 代码仓库 | github.com/wings1848/dsh-mcp-lazy |
| 许可证 | MIT |
| 主要语言 | main |
| 下载量 | 1 |
| GitHub 星标 | 4 |
| 最近推送 | 2026-09-16 |
| 收录日期 | 2026-09-19 |
| 分类 | 模型与提供方 |
事实信息来自公开插件目录快照(2026-10-01),介绍文案由本站再加工。
以下为插件仓库 README 全文(原始内容,由公开目录抓取整理)。
# dsh-mcp-lazy
[](https://github.com/wings1848/dsh-mcp-lazy/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/dsh-mcp-lazy)
[](https://nodejs.org)
[](https://github.com/wings1848/dsh-mcp-lazy/blob/main/LICENSE)
[](https://github.com/wings1848/dsh-mcp-lazy/blob/main/README-zh.md)
A lazy MCP gateway for [DeepSeek Harness](https://github.com/deepseek-ai). It puts **one**
tool in front of the model instead of N MCP tool schemas: servers start on first use, idle
out again, and their tool metadata is cached on disk so `search` and `describe` never spawn
anything.

## Why
`@deepseek-ai/dsh-mcp-client` connects every configured server at startup and registers every
one of its tools as a native tool. As its own README puts it, tool descriptions and input
schemas "enter every request while the tools are registered". A handful of servers therefore
costs thousands of tokens on every request and one resident child process each — whether or
not the model ever calls them.
This plugin keeps the model-facing surface at exactly one tool whose schema never changes,
discovers tools on demand from a disk cache, and only spawns a server when a call needs it.
## The numbers
Measured against `chrome-devtools-mcp@1.6.0` (29 tools), both sides rendered the same way —
JSON bytes of the tool definitions, then four bytes per token:
| | per request |
| --- | --- |
| native registration | 21252 bytes ≈ **5313 tokens** |
| this gateway | 1525 bytes ≈ **381 tokens** |
| saved | **92.8%** |
The gateway figure is constant: configuring ten more servers does not move it, because their
schemas are read on demand rather than sent every request.
**The honest counterweight.** The gateway costs a fixed 1525 bytes, so it wins only when a
server's rendered tool definitions exceed that. The bundled fixture offers 7 small tools and
the saving drops to 1.7%. A server with a couple of tiny tools would make the gateway a net
loss. Measure your own before assuming:
```bash
pnpm run measure:savings # local fixture
node scripts/measure-token-savings.mjs --npx
```
## Install
```bash
dsh plugin --profile add dsh-mcp-lazy
```
That installs the package into the profile and registers its bundle patch, which inserts the
`mcp-lazy` row. Then give it your servers in the profile's `cordis.patch.yml`:
```yaml
- id: mcp-lazy
config:
servers:
- serverName: chrome
transport: stdio
command: npx
args: ['-y', 'chrome-devtools-mcp@1.6.0']
lifecycle: lazy
- serverName: docs
transport: streamable-http
url: http://127.0.0.1:3000/mcp
```
Restart the profile. Every field is documented in [docs/configuration.md](https://github.com/wings1848/dsh-mcp-lazy/blob/main/docs/configuration.md).
### Moving servers off `@deepseek-ai/dsh-mcp-client`
Every writer of MCP configuration in this ecosystem emits a `@deepseek-ai/dsh-mcp-client` row:
the config-manager panel hardcodes that package name, `@hyzyn/dsh-codegraph` writes a managed
row, and a hand-written config follows the same convention. Such a row registers each MCP tool
as a real tool, so its schemas enter every request — and a server listed in **both** places
cancels the saving this plugin exists for, with no error and nothing to notice. (The `mcp({})`
status output does warn you — for any server the other plugin serves natively, whether or not
this one has it too — which is how you usually find out.)
`adopt` does the move for you:
```bash
dsh-mcp-lazy-adopt # dry run: prints the plan, writes nothing
dsh-mcp-lazy-adopt --write # applies it, with a timestamped backup of each file
```
Or, without leaving the session, as a slash command:
```
/mcp-adopt # dry run
/mcp-adopt apply # writes
```
The command drives the same CLI, so the plan, the backups and the write are one code path; a
successful `apply` is followed by a re-plan whose result is shown to you, so the answer to "did
it work?" is evidence rather than an assurance. It still requires the human to type it: nothing
runs at plugin startup.
It reads what is *actually* mounted (`dsh --profile
--dump-config`, so all four patch layers
are composed), marks each original row `disabled: true` **in place**, and appends the server to
this plugin's `servers` list. Nothing else in the file is touched — comments, blank lines and
`!!js` expressions are preserved byte for byte — and a row it cannot move safely is reported
with a reason instead of being guessed at. Run it while the host is stopped: the web profile
reloads its patch layer live, and `dsh-config-manager` rewrites the same file from its own
state.
**Check the blast radius before you apply.** The native row usually lives in the *home* patch,
which every profile reads, while this plugin's row lives in one profile's patch. Disabling the
former therefore takes that server away from every other profile as well — and those profiles
have no gateway to receive it, so they simply lose the capability. The plan names them:
```
⚠ the row being disabled lives in the home layer ~/.dsh/cordis.patch.yml, which every profile reads.
3 other profile(s) do not mount dsh-mcp-lazy, so they would lose codegraph with no replacement:
default, dsh-tui, headless.
Mount dsh-mcp-lazy in those profiles, or move the row into web's own layer, if they need it.
```
If any of them need the server, mount this plugin there too (or move the row) before applying.
A server that does move arrives with only the fields this plugin implements. Two of the other
plugin's fields are **not** implemented here — `reconnect` and `failOnStartupError` — and
carrying them over is an error rather than a no-op, so a row that sets either one is reported as
`unsupported-field` and left exactly where it is: no half-move, and no load failure from a
setting that would look configured and do nothing. A misspelled field name is reported the same
way. The per-row `id` is dropped, because it names the loader row rather than the server.
## What the model sees
One tool, always the same 11 parameters:
```
mcp({ search: "screenshot" }) # find a tool — reads the cache, starts nothing
mcp({ describe: "take_screenshot" }) # full argument schema
mcp({ tool: "take_screenshot" }) # call it — this is what spawns the server
mcp({ tool: "echo", server: "docs" }) # disambiguate a name two servers share
mcp({ connect: "chrome" }) # connect and refresh the cache, without calling
mcp({ instructions: "chrome" }) # the server's own usage notes
mcp({}) # status: tool count, connection state, cache age
```
Seven of the 11 are the actions above. The other four shape a `search` and are
never needed for anything else:
| Parameter | Meaning | Default | Limit |
| --- | --- | --- | --- |
| `regex` | Treat `search` as a regular expression rather than literal text. | `false` | — |
| `includeSchemas` | Include each match's parameter summary in the result. | `true` | — |
| `limit` | How many matches to return. | `12` | `40`; clamped into 1–40, never an error |
| `offset` | Skip this many matches, for paging through a long result. | `0` | — |
That one tool is the **whole** model-facing surface by default. `directTools` is
the opt-in that promotes chosen server tools into real native tools instead —
see [docs/configuration.md](https://github.com/wings1848/dsh-mcp-lazy/blob/main/docs/configuration.md)
for what it accepts, and why the default is the cheap choice.
## Documentation
| | |
| --- | --- |
| [docs/configuration.md](https://github.com/wings1848/dsh-mcp-lazy/blob/main/docs/configuration.md) | every field, the four lifecycle modes, output ceiling |
| [docs/troubleshooting.md](https://github.com/wings1848/dsh-mcp-lazy/blob/main/docs/troubleshooting.md) | failed servers, cold cache, name resolution |
| [docs/development.md](https://github.com/wings1848/dsh-mcp-lazy/blob/main/docs/development.md) | build, test, why `link-dsh` is mandatory |
| [docs/design/plan.md](https://github.com/wings1848/dsh-mcp-lazy/blob/main/docs/design/plan.md) | implementation plan and acceptance criteria |
| [docs/design/adopt-native-rows.md](https://github.com/wings1848/dsh-mcp-lazy/blob/main/docs/design/adopt-native-rows.md) | the `adopt` command: design, invariants, acceptance criteria, and what implementing it turned up |
| [docs/design/mcp-adopt-command.md](https://github.com/wings1848/dsh-mcp-lazy/blob/main/docs/design/mcp-adopt-command.md) | the `/mcp-adopt` slash command, and the adversarial review that changed it |
| [docs/design/parity-pi-mcp-adapter.md](https://github.com/wings1848/dsh-mcp-lazy/blob/main/docs/design/parity-pi-mcp-adapter.md) | module-by-module audit against `pi-mcp-adapter` v2.33.0 |
## Known limitations
The v1 boundary, stated plainly:
- **Tools only.** No MCP resources, prompts, sampling, or elicitation. Only `tools/list_changed`
is subscribed.
- **No OAuth.** Authentication is a plaintext `headers` entry or an environment variable.
- **Image and audio results are not forwarded.** They are projected to a one-line metadata
entry (type and byte count). Forwarding pixels needs attachment storage this plugin does
not implement.
- **No approval gate.** MCP calls follow your DSH permission preset.
- **No config interop.** It reads DSH-native config only; it will not import `.mcp.json`,
Cursor, Claude Code, Codex, or VS Code server lists.
- **The regex guard is deliberately narrow.** A 256-character cap plus a nested-quantifier
check, which does **not** catch overlapping alternation like `(a|aa)+` or polynomial
backtracking like `a*a*a*b`. A full analyser would mean a second runtime dependency.
- **No SSE or unix-socket transports.** `stdio` and `streamable-http` only.
- **`npx` is not resolved to the underlying binary**, so an `npx`-launched server costs one
extra Node parent process.
## Development
```bash
pnpm install
pnpm test # builds, relinks the peer packages, runs 354 tests
pnpm run check # typecheck, lint, build, then type-check the test sources
```
See [CONTRIBUTING.md](https://github.com/wings1848/dsh-mcp-lazy/blob/main/CONTRIBUTING.md).
## Star history
[图片: Star history chart]
## License
MIT — see [LICENSE](https://github.com/wings1848/dsh-mcp-lazy/blob/main/LICENSE). The connection supervisor, transport factory, and
environment-scrubbing rules derive from `@deepseek-ai/dsh-mcp-client`; the single-proxy-tool
gateway, metadata cache, and weighted search ranking derive from `pi-mcp-adapter`. Both are
MIT; their notices are reproduced in [THIRD_PARTY_NOTICES.md](https://github.com/wings1848/dsh-mcp-lazy/blob/main/THIRD_PARTY_NOTICES.md).
数据来源:公开的 DeepSeek Harness 插件目录与各插件 GitHub 仓库。本站为独立第三方目录,与 DeepSeek、幻方(High-Flyer)及插件作者均无隶属或背书关系。