dsh-mcp-lazyload
Verifieddsh-mcp-lazyload · v0.4.5 · MIT · Web UI
把 MCP 工具从全量常驻改为渐进式披露:全部工具名与一句话常驻(L0 目录),完整参数 schema 按需取(mcp_find detail),执行走 mcp_use,另在左侧栏提供只读状态面板。
Install
dsh plugin add dsh-mcp-lazyload Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
dsh-mcp-lazyload
把 MCP 工具从「全量常驻上下文」改成「按需加载」,并提供一个左侧栏状态面板。
要解决的问题
DSH 的 MCP 工具是全量常驻的。dsh-mcp-client 把每个 MCP 工具注册进全局工具表后,
tools.schemas() 无条件把全部工具的 JSON Schema 投影给模型,每一轮请求都带着。
本机实测(aarch64 Android,207 个 REA 工具):
| 项目 | 字节 | 折算 token | 常驻时机 |
|---|---|---|---|
| MCP 工具 schema | 820,278 | ≈240k | 每轮 |
| Skill 前置元数据 | 437 | 可忽略 | 每轮 |
| Skill 正文 | 74,537 | ≈20k | 仅触发时 |
上下文窗口 1,048,576 token,光工具定义就占 23%。
同一个产品里 Skill 有真正的渐进披露(437B 常驻 / 74KB 按需),MCP 没有。 本插件就是把 MCP 也改成渐进披露。
测量口径:原始 tools/list 响应是 3,423,371 字节,其中 outputSchema 占 74.8%,
而 DSH 从不外发这个字段。真实成本是 820KB,不要拿 3.4MB 说事。
装完什么变了:四层渐进披露
不是把 MCP 工具"藏起来",而是分层:默认只常驻便宜的那一层, 越贵的细节越往后放,由模型自己决定要不要付这笔钱。
| 层 | 内容 | 何时付 | 实测体积 |
|---|---|---|---|
| L0 常驻目录 | 每个工具一行:server/tool: 一句话 |
每轮 | 251 工具 29.3KB(实测) |
L1 mcp_find |
命中工具的名字 + 压平截断的描述 | 调用时 | 1~3KB |
L2 mcp_find detail |
单个工具的完整参数 schema | 真要填参数时 | 单条 2KB 级 |
L3 mcp_use |
转发执行结果 | 执行时 | — |
L0 是关键,也是唯一必须存在的层。没有它就不叫披露,叫藏起来让人猜: 模型不知道有哪些工具,只能先盲探一轮,而且它不知道自己不知道什么。 DSH 自己的 Skill 就是这样分层的——目录常驻、正文按需。
所以 L0 的取舍不是"列不列",而是每条留多少字。见下方
「catalogDescriptionMax 为什么默认 120」。
L2 有一条硬规矩:detail 一次只披露一个工具的 schema。命中多条时
返回收窄提示而不是把几十份 schema 一次倒出来,否则刚省下的常驻成本当场还回去。
目录随工具面实时变化(text 是函数,不是启动快照),服务器连上、重连、
新增工具都会反映到下一轮提示词;折叠撤销后目录块整个消失,
不会继续声称"这些工具已折叠"而实际全量可见。
两个入口工具
| 工具 | 参数 | 返回 |
|---|---|---|
mcp_find |
query / server / detail / limit |
工具名 + 一行描述;detail 收窄到单条时附完整参数 |
mcp_use |
server / tool / args |
转发执行结果 |
早期版本还有第三个 mcp_servers(服务器概览)。已删除:L0 目录本来就按
server/tool 列全,工具数在目录首尾也有,它提供的 folded / sample
目录里都能看出来,纯属同一份信息占两处额度。要看服务器粒度用左侧栏面板。
mcp_use 收的是原始名字(server + tool),内部拼 mcp__server__tool。
左侧栏面板
安装后左侧栏出现一行「MCP 懒加载」,点进去是只读状态面板:降幅百分比、 折叠/直连工具数、每个服务器的工具数与样例、以及两个折叠后工具的用法。
面板只是一个观测窗口。开关和逃生口仍然在配置里改,面板不提供编辑, 这样配置只有一份真源,不会出现「面板显示 A、实际跑 B」。
安装
dsh plugin --profile <profile> add dsh-mcp-lazyload
也可以钉版本:add dsh-mcp-lazyload@<version>。
包已发布到 npm,不需要本地路径。装了之后用 dsh plugin --profile <profile> list
可以确认它在列表里。
装完如果 client-hmr 是关的,需要刷新一次浏览器让新的客户端 bundle 生效。
宿主侧的折叠与会话无关,不需要重启。
配置
在 profile 的 cordis.patch.yml 里写。注意 patch 会整体替换目标行的 config,
所以想保留的键要一起重述:
- id: mcp-lazy
name: 'dsh-mcp-lazyload'
config:
enabled: true
alwaysVisible: [] # 这些服务器的工具保持直连,不折叠
allowDetail: true # 是否允许 mcp_find 返回完整参数 schema
maxFindResults: 40 # mcp_find 单次最多返回条数
catalog: true # L0 常驻目录(关掉就退回"全隐藏 + 靠盲探")
catalogDescriptionMax: 120 # 目录里每条摘要的字符上限;0 = 只列名字
catalogMaxTools: 600 # 超过这个数就压缩摘要;超过三倍才只留名字
catalogBudgetPct: 5 # 目录最多占上下文窗口百分之几;0 = 关闭这条约束
contextWindow: 200000 # 换算上面那个百分比用的窗口大小(token)
alwaysVisible 是必要的逃生口:某个 MCP 只有 3 个工具、且每轮都高频用,
折叠反而多一次往返,就把它加进来。
enabled: false 时行为与未安装完全一致(仍然注册两个入口工具,但不摘任何东西,
目录也不产出)。
relevanceFloor:按相对分数截断,而不是固定条数
默认 0.3:只保留分数不低于「最高分 × 0.3」的结果,0 表示关闭。
为什么不用固定条数,实测数据见下面「相关性下限」一节——简单说, 固定条数对宽查询不够收(33 条里 20 条之后全是噪声)、对窄查询是空切 (只有 15 条时第 16 条本来就不存在)。按比例切则宽窄自适应,实测目标工具零丢失。
catalogBudgetPct:为什么还需要一个百分比
catalogDescriptionMax 是绝对字符数,而"目录会不会挤爆上下文"是相对的。
同样的 29.3KB 目录:
| 上下文窗口 | 目录占上下文 | 是否合规(MCP 规范建议 1%~5%) |
|---|---|---|
| 200K | 4.47% | 合规 |
| 128K | 6.90% | 超了 |
所以配置里多了一层:按 catalogBudgetPct 与 contextWindow 反算每条摘要可用多少字,
再与 catalogDescriptionMax 取最小值(绝对上限仍然兜底)。
实测(251 工具):
200K 窗口 → 平均每行 119 字符,目录占 4.47%(不降档)
128K 窗口 → 平均每行 83 字符,目录占 5.03%(自动降档,工具名一个不丢)
默认 5%,取 MCP 官方 client-best-practices 建议区间的上沿 (Claude Code 默认阈值 10%, Hermes 默认 5%)。
contextWindow 为什么不自动探测:DSH 的 agent 对象不向插件暴露当前模型路由;
llm.resolveModelInfo(provider, model) 需要 provider/model,而 systemPrompt 组装时
(assemble(context))只拿得到 scope。与其写一个"看起来智能、实际恒回退默认值"的
探测,不如让它显式可配——换模型时按需改这个值。面板会显示实际生效的上限
(catalog.effectiveDescriptionMax),不是复述你配的 120。
catalogDescriptionMax 为什么默认 120
旧默认是 32,为了把降幅压进 95% 验收线。那个取舍是错的:它切的是成本, 不是信息密度。这批工具的描述中位数 289 字符,硬切 32 只能留下 11%,切出来是这样:
- rea/address_name: Resolve the primary analyzed na… ← 32 字
- rea/address_name: Resolve the primary analyzed name at a code or data address. ← 120 字
名词都没说完,模型照样认不出这工具干什么,于是仍需 mcp_find 盲探一次——
L0 存在的意义(免去盲探)根本没实现,省下来的钱没买到任何东西。
现在改成按句子边界截断(brief():优先在 .!?。!? 处收,退不到句子
边界才按词界切,并保护 {{...}} 占位不被切半)。同样的字符预算能保住首句语义。
本机真实 251 个 MCP 工具(rea 207 / github 26 / filesystem 14 / graphflow 4), 全量 schema 782KB:
| 摘要上限 | 目录体积 | 降幅 |
|---|---|---|
| 0(只列名字) | 6.7KB | 99.2% |
| 32(旧默认) | 13.8KB | 98.2% |
| 120(现默认) | 29.3KB | 96.2% |
| 160 | 42.3KB | 95.0% |
多花约 15KB,换每个工具都能直接读出用途。仍高于 95% 线。
实测首句长度均值 93 字符(中位 98),所以 120 字档基本完整保住首句,
长尾才牺牲参数细节——那些本来就该 mcp_find detail 现场取。
摘要上限为 0 时目录会自动切换成紧凑格式:按服务器分组、工具名逗号连接,
同样信息量比逐行 - server/tool 省掉一半以上骨架开销。
mcp_find 的检索:BM25
早期用 haystack.includes(query) 整串子串匹配。251 个工具下这几乎不可用——
"create pull request" 要求整串连续出现,而描述写成 Create a new pull request…,
多个 a 就零命中。实测 8 个典型查询里 6 个零命中。
现在换 BM25(k1=1.2,b=0.75),并对工具名给 2 倍词频权重(查 analyze_function
时名字命中的可信度远高于描述里顺带提到 analyze)。
实测同一批真实工具(251 个):
| 查询 | 旧 includes |
BM25 | BM25 首条 |
|---|---|---|---|
| create pull request | 0 | 42 | github/create_pull_request |
| analyze function | 0 | 19 | rea/analyze_function |
| search bytes | 0 | 29 | rea/search_bytes |
| decompile procedure | 0 | 25 | rea/batch_decompile |
| 抓包 | 0 | 21 | rea/inspect_pcap_capture |
| 字节签名 | 0 | 39 | rea/search_bytes |
中文查询怎么办
本机 251 个工具的描述 100% 是英文(实测 0 条含汉字),所以中文查询靠分词
永远切不出交集:安装应用 切出的是 安装/装应/应用,与英文描述交集为空,
BM25 零命中,子串回退搜的也是英文全文,同样零命中——中文用户拿到的就是空结果。
解法是 ZH_HINTS 词典:把中文意图映射成英文关键词,追加进查询词元。
第一版只有 34 条(抓包、字节、签名…),漏了「安装」「应用」这类最常用的词,
实测 安装应用 返回 0 条。现在扩到约 130 条,按真实语料覆盖到:
| 类别 | 例子 |
|---|---|
| 系统与设备 | 安装/卸载/应用/包名/设备/截图/点击/日志/进程 |
| 文件与包 | 文件/目录/拉取/推送/导出/打包/解包 |
| 逆向与二进制 | 反编译/反汇编/伪代码/函数/符号/地址/字节/加固/脱壳 |
| 调用与数据流 | 调用/调用链/调用者/交叉引用/追踪 |
| 运行时与调试 | 调试/断点/附加/会话/动态/模拟 |
| 网络与网页 | 抓包/请求/响应/接口/浏览器/证书 |
| 安卓专项 | 安装包/清单/资源/布局/控件/界面 |
| 安全与漏洞 | 漏洞/越权/绕过/壳检测 |
实测(251 个真实工具、12 个中文查询):前 5 命中率 11/12,安装应用
首条就是 install_adb_package。唯一没进前 5 的「列出设备」,首条是
list_adb_features——它其实是对的,是我的期望值写错了。
右侧英文词必须能在工具名或描述里真实命中,否则加了等于没加;测试会校验 每个右侧词在语料中确实存在。
仍然没有接翻译 API:多一次网络往返、把工具检索变成可用性依赖,
而词表法已经覆盖了这几类工程的绝大多数意图。长尾场景(比如中英混写、
口语化描述)仍要模型自己把意图转成英文关键词,这点写在 mcp_find 的描述里。
相关性下限:不再固定返回 N 条
固定条数切法对宽窄两类查询都不合适。实测 251 个工具上:
create pull request全量 33 条,第 20 名之后全是只蹭到 create 一个词 的无关工具(pull_adb_file、push_adb_file);hook android class全量只有 15 条,第 16 条本来就不存在。
固定 40 对前者不够收、对后者是空切。现在按相对分数截断:只保留
score >= 最高分 × relevanceFloor(默认 0.3)的结果。
| 查询 | 全量 | 保留下限后 | 目标工具排名 |
|---|---|---|---|
| create pull request | 33 | 14 | 1 |
| 抓包 | 28 | 11 | 1 |
| 字节签名 | 39 | 27 | 1 |
| hook android class | 15 | 11 | 1 |
| analyze function | 19 | 15 | 1 |
实测 23 个查询:平均返回条数 28.2 → 13.8(削掉 51%), 目标工具 23/23 全部保留,前 5 名命中率不变。
宽查询自动收窄、窄查询原样不动,不需要模型自己判断"结果碎不碎"。
被砍掉时返回值里带一句说明(另有 N 条低于相关性下限…),
因为不告诉它的话,模型会把"只返回 14 条"误解成"库里只有 14 条相关工具"。
想要冷门工具时把 limit 调大或换更准的关键词。
设 relevanceFloor: 0 可完全关闭,退回旧行为。
每条结果带 matchedTerms
返回给模型的每条结果附带 matchedTerms——本次查询命中了这条的哪些词。
只蹭到 create 一个泛词 vs 同时命中 search+bytes 两个区分性强的词,
可信度完全不同。模型看得到命中词,就能判断这条结果靠不靠得住,不用盲信排序。
实测 抓包 的首条 matchedTerms 是 capture,pcap,packet,
search bytes 的是 search,bytes。
调研过但没采用的方案
| 方案 | 结论 |
|---|---|
| 语义向量检索(embedding) | 环境不具备:无 onnxruntime / transformers.js,可用内存 2.3GB。且每次搜索从微秒级变成秒级,与"功耗均衡"的诉求相反 |
工具名 bigram(pull_request 作为一个概念) |
实测无收益:bigram 权重 0→6 时前 8 条顺序完全不变,Top1 一直 22/23。工具名已经是强信号,bigram 只是同比例放大 |
| Porter 词干化 | 实测有害:Top1 从 19/20 降到 16/20。procedure→procedur 之类会与描述里的其他词撞车 |
| IDF 加权覆盖率硬门槛 | 实测有害:中文查询被直接砍成 0 条(安装应用 的覆盖率只有 0.19,卡在 0.2 门槛下) |
| 短语邻近加分 | 实测微负:Top1 从 19 降到 18,batch_decompile 因短语邻近反超了正确的工具 |
结论:BM25 在这批高结构化的工具名语料上已经接近上限(Top1 22/23,
唯一未命中的 decompile procedure 让 batch_decompile 排第一本身也不算错)。
真正的可改进点在长尾噪声(固定条数)和中文覆盖(词表),
不是排序算法本身——所以这两项已实现,而没有为了"更强算法"引入更重的依赖。
两条必要的短路
精确名字:查询本身是某个工具的全名 / 原始名 / server/tool 时只回那一条。
不做这个的话,mcp__rea__address_name 会被分词成 mcp/rea/address/name,
凡描述含 address 的工具都得分——实测返回 251 条,用户给了精确名字却得到全库。
子串回退:BM25 零命中时退回原文 includes。这不是保险丝,是必需路径:
查询词元全被停用词过滤(the)时 BM25 返回 undefined;
OpenClaw 与 Hermes 也都保留同类回退,
理由是防零 IDF 退化。
机制
用官方 API ctx.tools.restrict({ deny }) 摘掉 mcp__*:
restrict → layer.restrictions.append
→ view(scope).visible 过滤(admits() 命中 deny 即跳过)
→ schemas(scope) 只投影 visible
→ 请求体 tools 数组里不再有这些工具
这是机制层真摘除,不是提示词层面的糊弄。
restrict 会影响执行(原「坑 8」结论是错的)
早期文档写过"restrict 只影响可见性、不影响执行,所以转发不需要绕过手段"。 这是错的,而且方向反了,照它写代码会得到必然失败的转发。
真实宿主 dsh-agent-loop/lib/index.js:778:
this.scope = createScope(loopCtx, this) // ← scope key 就是 agent 对象本身
于是 view(agent) 会应用本插件刚下的 deny,get(name, agent) 得 undefined,
dispatchToolBody 里 resolveExecution(name, exec.agent, ...) 抛
ToolNotFoundError → UNKNOWN_TOOL。实测(scope key = agent 对象):
view(agent) → []
get(name, agent) → undefined
execute(带 agent) → isError=true, code=UNKNOWN_TOOL
execute(不带 agent) → isError=false, 成功
这个结论极易搞反:若 scope key 用 {agentId:"x"} 这种独立对象、另造一个 agent,
则 scopeParents.get(agent) 查不到链 → 不应用 restriction → 看起来"带 agent 成功"。
我们曾在测试里用错 key,导致这条回退分支一次都测不到,还差点把它当死代码删掉。
因此 mcp_use 的转发是:先带 agent 试(权限 guard 照常生效),
仅当结果是 UNKNOWN_TOOL 且该名字在全局视图里确实存在时,才回退到不带 agent 再试。
test/real-runtime.mjs 对这两段都有断言:
"裸 execute(带 agent) 按预期失败 UNKNOWN_TOOL" + "mcp_use 回退后仍可执行"。
实现必须守住的坑
这些是实际运行验证出来的,静态读代码看不出来。
- schema 每个 object 都要显式写
additionalProperties,根节点和嵌套都一样,缺一个defineTool就抛JsonSchemaError。 - 自由值必须用
type: 'json',写{}会被拒。编译后它变成 annotation-only 节点。 tools.execute()的signal是必填。省略会在prepareExecution阶段抛Cannot read properties of undefined (reading 'aborted')。agents不能写进inject数组。服务缺失时 Cordis 会挂起整个插件,apply一次都不执行,表现是"装了但没反应",日志里也看不出原因。用ctx.get('agents')安全查询。restrict只能在 agent 作用域调,插件根ctx没有kScopesymbol,直接抛错。且agent.ctx也要用get()取服务。restrict校验名字必须已存在(restrictableNames)。MCP 是异步注册的,必须监听tools/change重算。restrict是追加语义(restrictions.append)。每次重算前必须先 dispose 上一轮的,否则越叠越多。同时要做签名去重,这既是性能也是打断tools/change→ 重算 →tools/change递归的关键。restrict拒绝空过滤器:{ deny: [] }会抛tools.restrict({}) is a no-op。所以没有可折叠工具时直接跳过,不要调。- 插件自己的入口工具名绝不能进 deny,否则会把自己隐藏掉。
已知限制
PTC 模式下折叠不可用。 比早期文档写的更严重——不是"降级失效",是整条链路断。
tools.mode = 'ptc' 时实测:
| 调用方式 | 结果 |
|---|---|
模型直调 mcp_use |
UNKNOWN_TOOL(PTC 只允许直调 run_code) |
模型直调 mcp__x__y |
UNKNOWN_TOOL |
run_code 内调 mcp_use |
进得来,但转发时 collapses() 判定 !nested && mode==='ptc' → UNKNOWN_TOOL |
run_code 内调 + 不带 agent |
能通,但绕过了 agent 作用域的权限 guard |
最后一行能"修好",但那等于静默关掉沙箱/审批。本插件不做这种绕过,宁可给出 明确报错:"在 PTC 模式下无法转发……把 tools 的 mode 改回 native"。
DSH 默认模式是 native,不受影响。
验收
三份测试,全部不需要 DSH 在跑:
npm test # 离线 7 份一起跑
npm run test:live # 另 2 份需要真 MCP 服务器在运行
node test/contract.test.mjs # 协议与行为契约(仿真 ToolRuntime)
node test/disclosure.mjs # L0→L3 分层披露逐层验证
node test/prompt.mjs # 提示词段落挂载与开关
node test/honest-metrics.mjs # 面板数字诚实性(专抓"报得好看但没生效")
node test/scope.mjs # 多 agent 作用域与去重
node test/scale.mjs # 真实量级下的降幅(210 工具 / 510KB)
node test/real-runtime.mjs # 真实 dsh-tools ToolRuntime(含 restrict 行为断言)
node test/live-mcp.mjs # 连真 filesystem MCP,全链路
node test/scale-live.mjs # 连真 MCP,225 工具 / 379KB
node test/budget-and-search.mjs # 上下文预算降档 + BM25 检索
指标诚实性(test/honest-metrics.mjs)
面板报的数字必须反映此刻的真实状态,不是插件的意图。这条测试专门抓 "报得好看但没生效",它抓出过三个真 bug:
| bug | 现象 | 修法 |
|---|---|---|
| 折叠未生效报 100% 降幅 | 无活跃 agent 时目录为空 → mcpAfter=0 |
未生效时 mcpAfter = mcpBefore,降幅 0 |
folded / catalog.tools 按名字数 |
折叠没生效也报 251 已折叠 | 按 enabled && foldingActive() 判定 |
| 测量缓存跨状态复用 | 折叠刚生效,面板 5 秒内仍报 0% | 缓存键加上 active |
判据是四个场景:无 agent(必须全 0)、有 agent(必须真降)、dispose 后
(必须退回 0)、enabled:false(必须与未安装一致)。降幅为 0 不是故障——
它说明此刻确实没省,这比虚报 100% 有用得多。
真实 MCP 服务器实测(test/live-mcp.mjs)
连本机配置里那个真的 filesystem MCP,走完整链路(握手 → 真工具注册 →
折叠 → 真 systemPrompt.assemble()):
14 个真工具,原始常驻 8,281 字节
→ schema 871 + L0 目录 1,647 = 2,518 字节,降幅 69.59%
69.59% 不是缺陷,是固定开销在小工具集上的摊销:入口工具的 schema 是 871 字节,占原始的 10.5%,光这一项就把降幅上限压到 89.5%;14 个工具摊不平它。 边际数字才是关键:
每工具 schema 592 字节 vs 目录 118 字节 → 折叠对每一个工具都是净收益
摊平固定开销所需工具数 ≈ 2 个
这里刻意不设总量百分比阈值。 早先有一条"降幅 >70%",在描述上限 32 字时
刚好贴着过;改成按句截断的 120 字后,目录从 1225 涨到 1647 字节,降幅落到
69.59%,差 34 字节就判失败。算一下就知道这条线在要求什么:
70% 留给目录的预算是 (1-0.7)×8281 - 871 = 1613 字节,摊到 14 个工具是
115 字节/工具,而 120 字描述实测 118 字节/工具——它实际是在要求把描述缩回 32 字。
用一个小分母上没有意义的百分比去倒逼实现牺牲模型识别力,是本末倒置。
所以小工具集看三件事,都不靠百分比:
绝对省下多少(本机 5763 字节)、边际是否划算、固定开销是否喧宾夺主。
总量 95% 的断言属于大工具面,由下面的 scale.mjs(210 工具 / 510KB)
和 scale-live.mjs(225 真工具 / 379KB,实测 96%)承担。
真实量级实测(test/scale.mjs,真实 ToolRuntime)
工具数 209 → 7
常驻 schema 543,274 → 1,494 字节
L0 目录 18,009 字节(206 行)
常驻总量 543,274 → 19,503 字节
降幅 96.41% (折算 token 约 159,786 → 5,736)
注意这里的 96.41% 已经把 L0 目录算进分母对面的成本。 早先报 99.68% 是因为当时只算 schema 那一半、把目录藏在括号外—— 那是把披露层的成本藏起来,数字好看但不可复核。
报告里本机 207 个 REA 工具是 820,278 字节;这里造的是同等量级(200 个 MCP 工具、 每个 30 个参数)。按同比例折算,820KB 会降到约 2.6KB。
报告第 7 节 8 条标准的落点
| # | 标准 | 落在哪份测试 | 实测 |
|---|---|---|---|
| 1 | mcp__* 在可见集里为 0 |
real-runtime / scale | 0 个 |
| 2 | 非 MCP 工具未受影响 | real-runtime | todo_write 等全部保留 |
| 3 | 折叠后仍可 mcp_use 调用,结果与直连一致 |
real-runtime | 直连==转发 |
| 4 | 新增/重连后新工具自动折叠 | real-runtime | 动态注册后即不可见 |
| 5 | enabled: false 行为与未安装一致 |
contract | restrict 调用 0 次 |
| 6 | disposer 能正确撤销 | real-runtime | agent 可见 MCP 0 → 4 |
| 7 | 常驻体积降幅 ≥95% | scale / scale-live | 96.41% / 96% |
| 8 | 插件异常不拖垮宿主 | real-runtime | 篡改 restrict 抛错,apply 不崩、入口工具仍在 |
contract.test.mjs 的覆盖:
- 客户端 bundle 按
__ModuleLoader__协议执行,注册了sidebar.panellist行与同 key 的main面板 mcp__*从可见集消失,非 MCP 工具与两个入口工具保留- 被 deny 的工具仍可通过
mcp_use转发执行 - 新增 MCP 工具自动折叠(
tools/change增量重算) alwaysVisible逃生口enabled: false时一次restrict都不调- 多 agent 各自折叠,签名未变不重复 restrict
- 两个入口工具的 schema 合规(
additionalProperties齐全、自由值走json) - 体积降幅 ≥95%
需要本地链接 peer 依赖才能跑(dsh-tools / schemastery 等):
D=<dsh 安装目录>/node_modules/@deepseek-ai
mkdir -p node_modules/@deepseek-ai
for p in schemastery dsh-tools cordis dsh-agent; do
ln -sfn "$D/$p" "node_modules/@deepseek-ai/$p"
done
许可
MIT