dsh-mcp-summon 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,49 @@
1
+ # Changelog
2
+
3
+ All notable changes to dsh-mcp-summon are documented here.
4
+
5
+ ## [0.1.0] - 2026-10-28
6
+
7
+ First release.
8
+
9
+ ### Added
10
+
11
+ - `mcp_summon` — the single model-facing tool (`list` / `summon` / `release` /
12
+ `status`) that keeps configured MCP servers dormant until the model activates
13
+ one, mid-session.
14
+ - Real native activation: `summon` spawns the stdio server, fetches
15
+ `tools/list`, and registers every tool via `ctx.tools.register` under
16
+ `mcp__<server>__<tool>` — real schemas, native UI rendering, per-tool
17
+ approval gates, image results intact — for the rest of the session.
18
+ `release` runs the exact disposers and kills the child.
19
+ - Server cards with honest cost lines: JSON bytes of
20
+ `{name, description, parameters}` ÷ 4, measured at first summon and cached
21
+ at `$DSH_HOME/storages/dsh-mcp-summon/cache.json`; cards are byte-stable
22
+ (sorted by `serverName`, frozen for the session) and a server is never
23
+ spawned just to card it.
24
+ - Per-server `disableTools` to keep chosen raw tool names off the surface.
25
+ - Optional `daemon:` block per server — for stdio servers that proxy to a
26
+ long-lived local service (e.g. `xerj mcp` → a xerj node on `:9200`). Summon
27
+ probes the health URL first, spawns the daemon detached only if nothing
28
+ answers, and waits for health before connecting; `release` stops the proxy
29
+ but never kills the shared daemon.
30
+ - Crash fallback: an unexpected child exit unregisters the server's tools and
31
+ returns it to dormant, with the reason kept for `status`.
32
+ - `scripts/measure-tokens.mjs` — per-server schema bytes/tokens for your own
33
+ configured servers (`--config`, `--fixture`, `--from-json`).
34
+ - Test suite (31 tests, `node --test`) including a dependency-free stdio MCP
35
+ fixture server exercised as a real child process: spawn → JSON-RPC → native
36
+ registration → release → kill, plus crash fallback, idempotence, and daemon
37
+ spawn / reuse-when-already-up / health-timeout paths.
38
+ - Vendored tool adapter, transport factory and naming rule from
39
+ `@deepseek-ai/dsh-mcp-client` 0.2.1-rc.3 (MIT; notice reproduced in
40
+ `THIRD_PARTY_NOTICES.md`).
41
+
42
+ ### Known limitations (v1)
43
+
44
+ - stdio transport only; tools only (no resources/prompts/sampling); no OAuth.
45
+ - No idle reclamation — release is explicit; no reconnect with backoff.
46
+ - Cards freeze within a session; fresh measurements land in the next session.
47
+ - Secrets passed via `args` land in argv (visible to `ps`); use `env`.
48
+
49
+ [0.1.0]: https://github.com/adithyanraj03/dsh-mcp-summon/releases/tag/v0.1.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 adithyanraj03
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,173 @@
1
- # Temporary Holding Version
1
+ <picture>
2
+ <source media="(prefers-color-scheme: dark)" srcset="assets/banner-dark.png">
3
+ <img alt="dsh-mcp-summon" src="assets/banner-light.png" width="1012">
4
+ </picture>
2
5
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
6
+ <br>
7
+
8
+ # dsh-mcp-summon
9
+
10
+ English | [中文](README.zh.md)
11
+
12
+ ![language](https://img.shields.io/badge/language-javascript-f7dc6f) ![license](https://img.shields.io/badge/license-MIT-blue) ![node](https://img.shields.io/badge/node-%E2%89%A5%2022-brightgreen) ![dsh](https://img.shields.io/badge/dsh-0.2.x-orange) ![tests](https://img.shields.io/badge/tests-passing-brightgreen) ![runtime](https://img.shields.io/badge/runtime-zero%20processes%20at%20load-brightgreen)
13
+
14
+ Dormant-by-default MCP for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): MCP servers cost nothing until the model **summons** one — and what arrives is not a proxy, it is the server's **real native tools**.
15
+
16
+ ## The problem
17
+
18
+ The official bridge (`@deepseek-ai/dsh-mcp-client`) connects every configured server at startup and registers **every** tool as a native tool, so every JSON schema enters **every model request forever** — whether the model ever calls them or not. A fat server pays that toll on every single turn:
19
+
20
+ | Server | Tools | Schema bytes | ≈ tokens / turn |
21
+ | --- | ---: | ---: | ---: |
22
+ | xerj (published schema, measured 2026-10-28) | 22 | 53,297 | **13,325** |
23
+ | chrome-devtools-mcp (measured by dsh-mcp-lazy) | 29 | 21,252 | 5,313 |
24
+ | this repo's test fixture | 5 | 902 | 226 |
25
+
26
+ ## What you get
27
+
28
+ At load the plugin registers exactly **one** tool, `mcp_summon`, whose description carries a card per configured server — capability summary, tool count, and an honest cost line measured the same way as the table above:
29
+
30
+ ```
31
+ Configured servers:
32
+ - xerj — Code and corpus search: definitions, hybrid search, field maps, hub corpora. (22 tools, ~13,325 tokens/turn when summoned)
33
+ - summon-fixture — Five demo tools: echo, add, now, image, fail. (5 tools, ~226 tokens/turn when summoned)
34
+ ```
35
+
36
+ The model activates servers itself, mid-session:
37
+
38
+ ```
39
+ mcp_summon({ action: "summon", server: "xerj" })
40
+ → summoned xerj: 22 tools registered as mcp__xerj__* (~13,325 tokens/turn
41
+ for the rest of this session, visible from your next turn).
42
+
43
+ mcp_summon({ action: "status" })
44
+ → Server states:
45
+ - xerj: summoned (22 tools, 53297 schema bytes ≈ 13325 tokens/turn, pid 41312)
46
+ - summon-fixture: dormant
47
+
48
+ mcp_summon({ action: "release", server: "xerj" })
49
+ → released xerj: its tools unregister from your next turn and its process stopped.
50
+ ```
51
+
52
+ After a summon the tools are **real native tools** — the same `ToolDefinition` adapter the official bridge uses (vendored, MIT notice reproduced): real schemas, native UI rendering, per-tool approval gates, image results admitted to durable storage intact. The harness re-assembles the tool set every turn, so they appear from the next turn and behave exactly like normally bridged MCP tools.
53
+
54
+ A crash or profile restart falls back to dormant-by-default — the safe direction. Session end releases everything.
55
+
56
+ ## Install
57
+
58
+ ```sh
59
+ dsh plugin --profile <your-profile> add dsh-mcp-summon
60
+ ```
61
+
62
+ Then list your servers in that profile's `cordis.patch.yml` (the bundle ships a commented example). Restart the profile. Servers stay dormant until summoned — nothing spawns at load.
63
+
64
+ ## Config reference
65
+
66
+ One row, `config.servers: [...]`. Each row is the same shape as `@deepseek-ai/dsh-mcp-client`'s, stdio-only in v1:
67
+
68
+ | Field | Required | Default | Notes |
69
+ | --- | --- | --- | --- |
70
+ | `serverName` | ✓ | — | `[A-Za-z0-9_-]{1,32}`; the `mcp__<server>__*` namespace |
71
+ | `transport` | ✓ | — | `stdio` (v1) |
72
+ | `command` | ✓ | — | program to spawn |
73
+ | `args` | | `[]` | secrets here land in argv — visible to `ps` |
74
+ | `env` | | `{}` | the only place for secrets: the parent env is credential-scrubbed before spawn |
75
+ | `cwd` | | `""` | |
76
+ | `summary` | | `""` | the card's capability paragraph; falls back to cached tool names |
77
+ | `disableTools` | | `[]` | raw tool names to keep off the surface — e.g. servers whose tool descriptions instruct the agent to auto-send data |
78
+ | `toolCallTimeoutMs` | | `60000` | |
79
+ | `daemon` | | — | a long-lived local service this server proxies to; see below |
80
+
81
+ ### `daemon:` — servers that proxy to a local service
82
+
83
+ Some stdio servers are just a proxy to a long-lived service you run separately — `xerj mcp` forwards every call to a xerj node on `http://localhost:9200`, and refuses to start one itself. A `daemon:` block makes summoning self-sufficient: before spawning the proxy, the plugin probes the health URL; if nothing answers it spawns the daemon **detached** (it outlives the plugin and the session), polls until healthy, then connects. `release` stops the proxy but **never kills the daemon** — it is shared infrastructure other tools and sessions may use.
84
+
85
+ ```yaml
86
+ - serverName: xerj
87
+ transport: stdio
88
+ command: xerj
89
+ args: ['mcp']
90
+ env: { XERJ_URL: 'http://localhost:9200' }
91
+ daemon:
92
+ command: xerj
93
+ args: ['--insecure', '--data-dir', 'C:/Users/adith/xerj-data']
94
+ healthUrl: 'http://localhost:9200/' # ANY HTTP response counts as up
95
+ startTimeoutMs: 15000
96
+ ```
97
+
98
+ If you already keep the node running yourself (terminal, Task Scheduler, service), the probe sees it and nothing is double-spawned — the block is a safety net, not a supervisor.
99
+
100
+ ## How it works
101
+
102
+ | Mechanism | dsh seam |
103
+ | --- | --- |
104
+ | One tool at load, zero processes | `ctx.tools.register` in `apply`; the only startup I/O is one read of the measurement cache |
105
+ | Daemon-backed servers | probe `healthUrl` → spawn detached + unref'd → poll until healthy → connect the proxy; `release` leaves the daemon running |
106
+ | Summoned tools appear next turn | `register` fires `tools/change` — "a global change concerns every agent's next assembly" — and the tool set is re-assembled per turn |
107
+ | Real native behaviour | the official bridge's tool adapter, transport factory and naming rule vendored from `@deepseek-ai/dsh-mcp-client` 0.2.1-rc.3 (MIT) |
108
+ | Honest cost lines | JSON bytes of `{name, description, parameters}` ÷ 4 — exactly the fields the registry's `schemas()` whitelists onto the wire |
109
+ | Byte-stable cards | sorted by `serverName`, frozen for the session; a fresh measurement lands in the **next** session's cards from `$DSH_HOME/storages/dsh-mcp-summon/cache.json` — a server is never spawned just to card it |
110
+ | Release | the exact disposers `ctx.tools.register` returned, then `transport.close()` kills the child |
111
+ | Crash | `onclose` → unregister → dormant, reason kept for `status` |
112
+
113
+ Activating a server changes the tool block once, so that turn's prompt-cache prefix is invalidated once — that is the documented price of activation, and it is paid whether you summon or the official bridge registers at startup.
114
+
115
+ ## Measurements
116
+
117
+ `node scripts/measure-tokens.mjs` prints per-server numbers for **your** configured servers (same methodology: rendered JSON bytes ÷ 4 — a deterministic proxy, not a BPE count):
118
+
119
+ ```sh
120
+ node scripts/measure-tokens.mjs --fixture
121
+ node scripts/measure-tokens.mjs --config ~/.dsh/profiles/web/cordis.patch.yml
122
+ node scripts/measure-tokens.mjs --from-json https://xerj.org/docs/agents/schemas/mcp-tools.json --label xerj
123
+ ```
124
+
125
+ Measured on this repo (2026-10-28): fixture 5 tools = 902 B ≈ 226 tokens/turn; xerj 22 tools = 53,297 B ≈ 13,325 tokens/turn. The permanent surface of `mcp_summon` itself — header plus two cards — is 1,591 B ≈ **398 tokens/turn**, and it grows only with the number of configured servers, never with their size.
126
+
127
+ ## dsh-mcp-summon vs dsh-mcp-lazy
128
+
129
+ [dsh-mcp-lazy](https://github.com/wings1848/dsh-mcp-lazy) is the other lazy-MCP plugin for dsh — a permanent **gateway**: one `mcp({search/describe/tool})` proxy tool, schemas fetched on demand, ~92.8% savings measured. Both keep idle servers at ~0. The difference is what happens **after activation**, and their README concedes it:
130
+
131
+ | Dimension | dsh-mcp-lazy (gateway) | dsh-mcp-summon (this) |
132
+ | --- | --- | --- |
133
+ | Idle cost per configured server | ~0 (disk-cached metadata) | 0 (dormant, never spawned) |
134
+ | Permanent model surface | 1 proxy tool, ~381 tok always | 1 summon tool, ~398 tok with 2 servers |
135
+ | After activation | schemas fetched per call through the proxy; calls go through `mcp({tool})` | real native tools: real schemas, native UI, per-tool approval gates, image results intact |
136
+ | Active-session cost | small proxy overhead per call | full schema tokens/turn for the rest of the session |
137
+ | Idle reclamation | yes (default 10 min) | no — release is explicit (v1) |
138
+ | Transports (v1) | stdio + streamable-http | stdio only |
139
+ | Wins when | many servers, each rarely used | one server used hard for a whole session |
140
+
141
+ If you keep 10 servers configured and touch one occasionally, take the gateway — it is strictly cheaper. If you summon xerj and live in it for two hours, native tools are worth their tokens: the model sees the real schema, calls `mcp__xerj__xerj_search` directly, and every result renders and gates like a first-class citizen.
142
+
143
+ ## Known limitations
144
+
145
+ The v1 boundary, stated plainly:
146
+
147
+ - **stdio only.** No `streamable-http` rows yet.
148
+ - **Tools only.** No MCP resources, prompts, sampling or elicitation.
149
+ - **No OAuth.** Auth is whatever the row's `env` can carry.
150
+ - **No idle reclamation.** A summoned server stays until you release it or the session ends.
151
+ - **No reconnect with backoff.** A crashed server goes dormant with the reason in `status`; re-summon to retry.
152
+ - **Cards are frozen within a session.** A first summon's measurement shows up in the next session's cards (mid-session numbers are one `status` call away).
153
+ - **A secret passed through `args` lands in argv**, readable by `ps`; `env` is the safe path.
154
+ - **`mcp_summon`'s own description is a permanent cost** (~400 tokens with two servers) — the floor this plugin pays so the model can decide to activate anything at all.
155
+
156
+ ## Development
157
+
158
+ ```sh
159
+ pnpm install
160
+ pnpm test # 27 tests: naming, cards+cache, dispatch, and a real stdio child
161
+ # (spawn → JSON-RPC → native registration → release → kill)
162
+ pnpm run measure # per-server bytes/tokens
163
+ pnpm run banners # re-render assets/banner-*.png from the SVGs
164
+ ```
165
+
166
+ ## Requirements
167
+
168
+ - dsh `0.2.0-rc.1` or newer (developed and tested on `0.2.1-rc.3`)
169
+ - Node.js 22 or newer
170
+
171
+ ## Licence
172
+
173
+ MIT — see [LICENSE](LICENSE). [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) carries the notice for the vendored `@deepseek-ai/dsh-mcp-client` code.
package/README.zh.md ADDED
@@ -0,0 +1,172 @@
1
+ <picture>
2
+ <source media="(prefers-color-scheme: dark)" srcset="assets/banner-dark.png">
3
+ <img alt="dsh-mcp-summon" src="assets/banner-light.png" width="1012">
4
+ </picture>
5
+
6
+ <br>
7
+
8
+ # dsh-mcp-summon
9
+
10
+ [English](README.md) | 中文
11
+
12
+ ![language](https://img.shields.io/badge/language-javascript-f7dc6f) ![license](https://img.shields.io/badge/license-MIT-blue) ![node](https://img.shields.io/badge/node-%E2%89%A5%2022-brightgreen) ![dsh](https://img.shields.io/badge/dsh-0.2.x-orange) ![tests](https://img.shields.io/badge/tests-passing-brightgreen) ![runtime](https://img.shields.io/badge/runtime-%E5%8A%A0%E8%BD%BD%E6%9C%9F%E9%9B%B6%E8%BF%9B%E7%A8%8B-brightgreen)
13
+
14
+ 面向 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 的「默认休眠」MCP 插件:MCP 服务器在被模型**召唤**之前零成本——而召唤后到来的是该服务器的**真正原生工具**,不是代理。
15
+
16
+ ## 问题
17
+
18
+ 官方桥(`@deepseek-ai/dsh-mcp-client`)在启动时连接所有已配置的服务器,并把**每个**工具注册为原生工具,于是所有 JSON schema **永远进入每一次模型请求**——无论模型是否调用过它们。一个「胖」服务器每一轮都在交这笔税:
19
+
20
+ | 服务器 | 工具数 | schema 字节 | ≈ tokens / 轮 |
21
+ | --- | ---: | ---: | ---: |
22
+ | xerj(官方发布 schema,2026-10-28 实测) | 22 | 53,297 | **13,325** |
23
+ | chrome-devtools-mcp(dsh-mcp-lazy 实测) | 29 | 21,252 | 5,313 |
24
+ | 本仓库测试 fixture | 5 | 902 | 226 |
25
+
26
+ ## 你得到什么
27
+
28
+ 加载时插件只注册**一个**工具 `mcp_summon`,其描述为每个已配置服务器携带一张卡片——能力摘要、工具数量,以及按上表同一方法实测的诚实成本行:
29
+
30
+ ```
31
+ Configured servers:
32
+ - xerj — Code and corpus search: definitions, hybrid search, field maps, hub corpora. (22 tools, ~13,325 tokens/turn when summoned)
33
+ - summon-fixture — Five demo tools: echo, add, now, image, fail. (5 tools, ~226 tokens/turn when summoned)
34
+ ```
35
+
36
+ 模型可以在会话中途自己激活服务器:
37
+
38
+ ```
39
+ mcp_summon({ action: "summon", server: "xerj" })
40
+ → summoned xerj: 22 tools registered as mcp__xerj__* (~13,325 tokens/turn
41
+ for the rest of this session, visible from your next turn).
42
+
43
+ mcp_summon({ action: "status" })
44
+ → Server states:
45
+ - xerj: summoned (22 tools, 53297 schema bytes ≈ 13325 tokens/turn, pid 41312)
46
+ - summon-fixture: dormant
47
+
48
+ mcp_summon({ action: "release", server: "xerj" })
49
+ → released xerj: its tools unregister from your next turn and its process stopped.
50
+ ```
51
+
52
+ 召唤之后,这些工具就是**真正的原生工具**——使用与官方桥相同的 `ToolDefinition` 适配器(vendored,已按要求保留 MIT 声明):真实 schema、原生 UI 渲染、逐工具审批门、图片结果完整进入持久存储。harness 每一轮重新组装工具集,因此它们从下一轮出现,行为与正常桥接的 MCP 工具完全一致。
53
+
54
+ 崩溃或 profile 重启会退回「默认休眠」——安全的方向。会话结束时全部释放。
55
+
56
+ ## 安装
57
+
58
+ ```sh
59
+ dsh plugin --profile <your-profile> add dsh-mcp-summon
60
+ ```
61
+
62
+ 然后在该 profile 的 `cordis.patch.yml` 里列出你的服务器(bundle 自带注释示例)。重启 profile。服务器在召唤前保持休眠——加载期零进程。
63
+
64
+ ## 配置参考
65
+
66
+ 一行配置:`config.servers: [...]`。每行与 `@deepseek-ai/dsh-mcp-client` 同形,v1 仅支持 stdio:
67
+
68
+ | 字段 | 必填 | 默认 | 说明 |
69
+ | --- | --- | --- | --- |
70
+ | `serverName` | ✓ | — | `[A-Za-z0-9_-]{1,32}`;即 `mcp__<server>__*` 命名空间 |
71
+ | `transport` | ✓ | — | `stdio`(v1) |
72
+ | `command` | ✓ | — | 要 spawn 的程序 |
73
+ | `args` | | `[]` | 密钥写在这里会进入 argv——`ps` 可见 |
74
+ | `env` | | `{}` | 密钥唯一安全的位置:spawn 前父环境会做凭据清洗 |
75
+ | `cwd` | | `""` | |
76
+ | `summary` | | `""` | 卡片的能力段落;缺省回退到缓存的工具名 |
77
+ | `disableTools` | | `[]` | 不上面的原始工具名——例如描述里指示 agent 自动外发数据的工具 |
78
+ | `toolCallTimeoutMs` | | `60000` | |
79
+ | `daemon` | | — | 该 server 转发到的常驻本地服务;见下 |
80
+
81
+ ### `daemon:` —— 转发到本地服务的 server
82
+
83
+ 有些 stdio server 只是一个常驻服务的代理——`xerj mcp` 把每次调用转发到 `http://localhost:9200` 上的 xerj 节点,而它自己不会启动节点。`daemon:` 让 summon 自给自足:在启动代理之前,插件先探测 health URL;若无响应,就以 **detached** 方式启动 daemon(它比插件和会话都活得久),轮询到健康后再连接。`release` 会停掉代理,但**绝不杀 daemon**——它是其他工具和会话共用的基础设施。
84
+
85
+ ```yaml
86
+ - serverName: xerj
87
+ transport: stdio
88
+ command: xerj
89
+ args: ['mcp']
90
+ env: { XERJ_URL: 'http://localhost:9200' }
91
+ daemon:
92
+ command: xerj
93
+ args: ['--insecure', '--data-dir', 'C:/Users/adith/xerj-data']
94
+ healthUrl: 'http://localhost:9200/' # 任何 HTTP 响应都算健康
95
+ startTimeoutMs: 15000
96
+ ```
97
+
98
+ 如果你自己已经开着节点(终端、计划任务、服务),探测会看到它,不会重复启动——这个块是安全网,不是进程管理器。
99
+
100
+ ## 工作原理
101
+
102
+ | 机制 | dsh 接缝 |
103
+ | --- | --- |
104
+ | 加载期一个工具、零进程 | `apply` 中 `ctx.tools.register`;唯一的启动 I/O 是读一次测量缓存 |
105
+ | 召唤的工具下一轮出现 | `register` 触发 `tools/change`——「全局变更影响每个 agent 的下一次组装」——工具集每轮重组 |
106
+ | 真正的原生行为 | 从 `@deepseek-ai/dsh-mcp-client` 0.2.1-rc.3 vendored 的工具适配器、传输工厂与命名规则(MIT) |
107
+ | 诚实的成本行 | `{name, description, parameters}` 的 JSON 字节 ÷ 4——正是 registry `schemas()` 白名单发到线上的字段 |
108
+ | 卡片字节稳定 | 按 `serverName` 排序、会话内冻结;新测量值经 `$DSH_HOME/storages/dsh-mcp-summon/cache.json` 进入**下一次**会话的卡片——绝不会仅为制作卡片而 spawn 服务器 |
109
+ | 释放 | 执行 `ctx.tools.register` 返回的精确 disposer,然后 `transport.close()` 杀掉子进程 |
110
+ | 崩溃 | `onclose` → 注销 → dormant,原因保留在 `status` 里 |
111
+
112
+ 激活会一次性改变工具块,因此那一轮的 prompt-cache 前缀失效一次——这是激活的既定代价,无论召唤还是官方桥启动注册都要付。
113
+
114
+ ## 测量
115
+
116
+ `node scripts/measure-tokens.mjs` 为**你的**已配置服务器打印每服务器数字(同一方法论:渲染 JSON 字节 ÷ 4——确定性代理,不是 BPE 计数):
117
+
118
+ ```sh
119
+ node scripts/measure-tokens.mjs --fixture
120
+ node scripts/measure-tokens.mjs --config ~/.dsh/profiles/web/cordis.patch.yml
121
+ node scripts/measure-tokens.mjs --from-json https://xerj.org/docs/agents/schemas/mcp-tools.json --label xerj
122
+ ```
123
+
124
+ 本仓库实测(2026-10-28):fixture 5 工具 = 902 B ≈ 226 tokens/轮;xerj 22 工具 = 53,297 B ≈ 13,325 tokens/轮。`mcp_summon` 自身的常驻表面(头部 + 两张卡片)为 1,591 B ≈ **398 tokens/轮**,只随服务器数量增长,与服务器大小无关。
125
+
126
+ ## dsh-mcp-summon 与 dsh-mcp-lazy 的对比
127
+
128
+ [dsh-mcp-lazy](https://github.com/wings1848/dsh-mcp-lazy) 是 dsh 的另一款惰性 MCP 插件——常驻**网关**:一个 `mcp({search/describe/tool})` 代理工具,schema 按需获取,实测节省约 92.8%。两者都让闲置服务器接近零成本,区别在**激活之后**,其 README 自己也承认:
129
+
130
+ | 维度 | dsh-mcp-lazy(网关) | dsh-mcp-summon(本插件) |
131
+ | --- | --- | --- |
132
+ | 每个已配置服务器的闲置成本 | ~0(磁盘缓存元数据) | 0(休眠,从不 spawn) |
133
+ | 常驻模型表面 | 1 个代理工具,恒定 ~381 tok | 1 个召唤工具,2 服务器时 ~398 tok |
134
+ | 激活之后 | schema 经代理按次获取;调用走 `mcp({tool})` | 真原生工具:真实 schema、原生 UI、逐工具审批门、图片结果完整 |
135
+ | 活跃会话成本 | 每次调用少量代理开销 | 本会话剩余时间全额 schema tokens/轮 |
136
+ | 闲置回收 | 有(默认 10 分钟) | 无——释放是显式的(v1) |
137
+ | 传输(v1) | stdio + streamable-http | 仅 stdio |
138
+ | 何时占优 | 服务器多、每个偶尔用 | 一个服务器整会话高强度用 |
139
+
140
+ 如果你配了 10 台服务器只是偶尔碰一台,选网关——严格更便宜。如果你召唤 xerj 并在里面工作两小时,原生工具值这些 tokens:模型看到真实 schema,直接调 `mcp__xerj__xerj_search`,每个结果都按一等公民渲染和审批。
141
+
142
+ ## 已知限制
143
+
144
+ v1 边界,直说:
145
+
146
+ - **仅 stdio。** 暂不支持 `streamable-http`。
147
+ - **仅工具。** 无 MCP resources、prompts、sampling、elicitation。
148
+ - **无 OAuth。** 认证只能靠行内 `env`。
149
+ - **无闲置回收。** 召唤后保持到显式释放或会话结束。
150
+ - **无退避重连。** 崩溃的服务器退回 dormant,原因在 `status`;重新召唤即重试。
151
+ - **卡片会话内冻结。** 首次召唤的测量值出现在下一次会话的卡片里(会话内数字一次 `status` 即得)。
152
+ - **写在 `args` 里的密钥进入 argv**,`ps` 可读;`env` 才是安全路径。
153
+ - **`mcp_summon` 自身描述是常驻成本**(两服务器时约 400 tokens)——这是让模型能够决定激活任何东西的地板价。
154
+
155
+ ## 开发
156
+
157
+ ```sh
158
+ pnpm install
159
+ pnpm test # 27 个测试:命名、卡片+缓存、分派,以及真实 stdio 子进程
160
+ # (spawn → JSON-RPC → 原生注册 → 释放 → 杀进程)
161
+ pnpm run measure # 每服务器字节/tokens
162
+ pnpm run banners # 从 SVG 重新渲染 assets/banner-*.png
163
+ ```
164
+
165
+ ## 环境要求
166
+
167
+ - dsh `0.2.0-rc.1` 或更新(开发与测试基于 `0.2.1-rc.3`)
168
+ - Node.js 22 或更新
169
+
170
+ ## 许可
171
+
172
+ MIT — 见 [LICENSE](LICENSE)。[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) 载有 vendored `@deepseek-ai/dsh-mcp-client` 代码的声明。
@@ -0,0 +1,50 @@
1
+ # Third-party notices
2
+
3
+ dsh-mcp-summon is MIT-licensed (see [LICENSE](LICENSE)). It vendors source from,
4
+ and depends on, the following.
5
+
6
+ ## Vendored source
7
+
8
+ ### @deepseek-ai/dsh-mcp-client 0.2.1-rc.3
9
+
10
+ `src/naming.js` and `src/adapter.js` are vendored and trimmed (stdio-only,
11
+ tools-only) from `@deepseek-ai/dsh-mcp-client`
12
+ (https://github.com/deepseek-ai/deepseek-harness, `packages/mcp/mcp-client`),
13
+ Copyright (c) 2026 DeepSeek, MIT License. Function bodies are kept
14
+ near-verbatim so summoned tools behave exactly like natively bridged ones and
15
+ public tool names are byte-identical for the same `(serverName, rawName)`.
16
+
17
+ > MIT License
18
+ >
19
+ > Copyright (c) 2026 DeepSeek
20
+ >
21
+ > Permission is hereby granted, free of charge, to any person obtaining a copy
22
+ > of this software and associated documentation files (the "Software"), to deal
23
+ > in the Software without restriction, including without limitation the rights
24
+ > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
25
+ > copies of the Software, and to permit persons to whom the Software is
26
+ > furnished to do so, subject to the following conditions:
27
+ >
28
+ > The above copyright notice and this permission notice shall be included in all
29
+ > copies or substantial portions of the Software.
30
+ >
31
+ > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32
+ > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33
+ > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34
+ > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35
+ > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36
+ > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37
+ > SOFTWARE.
38
+
39
+ ## Runtime dependencies
40
+
41
+ - `@modelcontextprotocol/client` 2.2.0 — the MCP TypeScript SDK client
42
+ (https://github.com/modelcontextprotocol/typescript-sdk). Licensed under a
43
+ MIT/Apache-2.0 transition dual-license per its LICENSE; used under the MIT
44
+ terms.
45
+ - `@deepseek-ai/dsh-tools`, `@deepseek-ai/dsh-subprocess`,
46
+ `@deepseek-ai/dsh-attachment` (optional) — DeepSeek Harness packages,
47
+ Copyright (c) 2026 DeepSeek, MIT License. Declared as peer dependencies;
48
+ they resolve from the host installation.
49
+ - `@deepseek-ai/schemastery` — Copyright (c) 2021-present Shigma, MIT License.
50
+ - `@deepseek-ai/cordis` — the plugin framework, MIT License.
Binary file
@@ -0,0 +1,18 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
2
+ <defs>
3
+ <radialGradient id="glow" cx="0.5" cy="0.5" r="0.5">
4
+ <stop offset="0" stop-color="#2ee6c6" stop-opacity="0.55"/>
5
+ <stop offset="1" stop-color="#2ee6c6" stop-opacity="0"/>
6
+ </radialGradient>
7
+ </defs>
8
+ <rect width="512" height="512" rx="112" fill="#0e192b"/>
9
+ <circle cx="120" cy="392" r="78" fill="url(#glow)"/>
10
+ <g fill="none" stroke="#2ee6c6" stroke-linecap="round" stroke-width="26">
11
+ <path d="M120 302 A90 90 0 0 1 210 392" opacity="0.35"/>
12
+ <path d="M120 242 A150 150 0 0 1 270 392" opacity="0.55"/>
13
+ <path d="M120 182 A210 210 0 0 1 330 392" opacity="0.8"/>
14
+ <path d="M120 122 A270 270 0 0 1 390 392" opacity="1"/>
15
+ </g>
16
+ <circle cx="120" cy="392" r="34" fill="#2ee6c6"/>
17
+ <path d="M390 78 L400 110 L432 120 L400 130 L390 162 L380 130 L348 120 L380 110 Z" fill="#fbbf24"/>
18
+ </svg>
@@ -0,0 +1,39 @@
1
+ # dsh-mcp-summon: dormant-by-default MCP for dsh. The plugin registers exactly
2
+ # one model-facing tool — `mcp_summon` — whose description carries a card per
3
+ # configured server. Summoning a server spawns it, fetches its tool list, and
4
+ # registers every tool as a real native tool (`mcp__<server>__<tool>`) for the
5
+ # rest of the session; releasing unregisters and kills it.
6
+ #
7
+ # `name` MUST be the package name: the loader imports this exact specifier.
8
+ # Server rows use the same shape as '@deepseek-ai/dsh-mcp-client'
9
+ # (serverName / transport / command / args / env / cwd); v1 is stdio-only.
10
+ - insert:
11
+ - id: dsh-mcp-summon
12
+ name: 'dsh-mcp-summon'
13
+ config:
14
+ servers: []
15
+ # Example — uncomment and point at your own servers:
16
+ # servers:
17
+ # - serverName: demo
18
+ # transport: stdio
19
+ # command: npx
20
+ # args: ['-y', '@modelcontextprotocol/server-everything']
21
+ # summary: >-
22
+ # Reference MCP server: echo, add, long-running ticks, annotations,
23
+ # icons — useful for testing anything MCP-shaped.
24
+ # disableTools: [] # raw tool names to keep off the surface
25
+ # - serverName: my-server
26
+ # transport: stdio
27
+ # command: node
28
+ # args: ['C:/path/to/server.mjs']
29
+ # env: {} # secrets only here: args land in argv (visible to ps)
30
+ # cwd: ''
31
+ # toolCallTimeoutMs: 60000
32
+ # # Optional: a long-lived local service this server proxies to. Summon
33
+ # # probes healthUrl first, spawns the daemon detached only if nothing
34
+ # # answers, and waits for health; release never kills it.
35
+ # # daemon:
36
+ # # command: xerj
37
+ # # args: ['--insecure', '--data-dir', 'C:/Users/adith/xerj-data']
38
+ # # healthUrl: 'http://localhost:9200/'
39
+ # # startTimeoutMs: 15000
package/locale/en.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "MCP Summon",
4
+ "description": "MCP servers stay dormant until the model summons them. One mcp_summon tool activates a server and its tools become real native tools for the rest of the session; release to unregister and reclaim."
5
+ }
6
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "MCP 召唤",
4
+ "description": "MCP 服务器默认休眠,由模型按需召唤。一个 mcp_summon 工具激活服务器后,其工具在本次会话剩余时间里成为真正的原生工具;释放即注销并回收。"
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,6 +1,85 @@
1
1
  {
2
2
  "name": "dsh-mcp-summon",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Dormant-by-default MCP for DeepSeek Harness: one mcp_summon tool; summoning a server registers its tools as real native tools (real schemas, native UI, approval gates, image results intact) for the rest of the session; release to reclaim.",
5
+ "icon": "assets/icon.png",
6
+ "type": "module",
7
+ "main": "./src/index.js",
8
+ "exports": {
9
+ ".": "./src/index.js",
10
+ "./cordis.patch.yml": "./cordis.patch.yml",
11
+ "./locale/*.json": "./locale/*.json",
12
+ "./icon": "./assets/icon.svg",
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "src",
17
+ "cordis.patch.yml",
18
+ "locale/*.json",
19
+ "assets/icon.svg",
20
+ "assets/icon.png",
21
+ "README.md",
22
+ "LICENSE",
23
+ "THIRD_PARTY_NOTICES.md",
24
+ "CHANGELOG.md"
25
+ ],
26
+ "scripts": {
27
+ "test": "node --test \"test/*.test.mjs\"",
28
+ "measure": "node scripts/measure-tokens.mjs",
29
+ "banners": "node scripts/make-banners.mjs"
30
+ },
31
+ "dsh": {
32
+ "bundle": {
33
+ "patch": "./cordis.patch.yml"
34
+ }
35
+ },
36
+ "engines": {
37
+ "node": ">=22",
38
+ "dsh": ">=0.2.0-rc.1"
39
+ },
40
+ "peerDependencies": {
41
+ "@deepseek-ai/cordis": "^4.0.2",
42
+ "@deepseek-ai/dsh-attachment": "^0.2.0-rc.1",
43
+ "@deepseek-ai/dsh-subprocess": "^0.2.0-rc.1",
44
+ "@deepseek-ai/dsh-tools": "^0.2.0-rc.1",
45
+ "@deepseek-ai/schemastery": "^3.18.2"
46
+ },
47
+ "peerDependenciesMeta": {
48
+ "@deepseek-ai/dsh-attachment": {
49
+ "optional": true
50
+ }
51
+ },
52
+ "dependencies": {
53
+ "@modelcontextprotocol/client": "2.2.0"
54
+ },
55
+ "devDependencies": {
56
+ "@deepseek-ai/cordis": "^4.0.2",
57
+ "@deepseek-ai/dsh-attachment": "^0.2.0-rc.1",
58
+ "@deepseek-ai/dsh-subprocess": "^0.2.0-rc.1",
59
+ "@deepseek-ai/dsh-tools": "^0.2.0-rc.1",
60
+ "@deepseek-ai/schemastery": "^3.18.2",
61
+ "@resvg/resvg-js": "^2.6.2",
62
+ "js-yaml": "^4.1.0"
63
+ },
64
+ "keywords": [
65
+ "dsh-plugin",
66
+ "dsh",
67
+ "deepseek-harness",
68
+ "mcp",
69
+ "model-context-protocol",
70
+ "lazy",
71
+ "token-efficient",
72
+ "context-engineering",
73
+ "tool-registration"
74
+ ],
75
+ "repository": {
76
+ "type": "git",
77
+ "url": "git+https://github.com/adithyanraj03/dsh-mcp-summon.git"
78
+ },
79
+ "homepage": "https://adithyanraj03.github.io/dsh-mcp-summon/",
80
+ "bugs": {
81
+ "url": "https://github.com/adithyanraj03/dsh-mcp-summon/issues"
82
+ },
83
+ "author": "Adithya N Raj (adithyanraj03)",
84
+ "license": "MIT"
85
+ }