DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

anyi-520 /

anyi-520/dsh-lazy-toolbox

Verified

DeepSeek Harness 插件:把 skill 与 MCP 从默认工具定义里抽出来,改成按需热加载的懒工具箱(skill / mcp 分开列出,一句话说明用途,用时注入、用完卸载)

★ 1 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@8dbf8d56

dsh-lazy-toolbox

仓库:https://github.com/anyi-520/dsh-lazy-toolbox · 安装:

git clone https://github.com/anyi-520/dsh-lazy-toolbox.git
node dsh-lazy-toolbox/bin/cli.mjs wire-profile   # 挂进 profile 并加好配置行

也可以下 Release 附件 解压后同样一条命令;npm 发布后可直接 dsh plugin add dsh-lazy-toolbox。

把 skill 和 MCP 从「每个对话都塞进工具定义」改成「列个目录,用时才注入」。

DSH(DeepSeek Harness)默认会把所有 skill、所有 MCP 服务的完整工具定义(名字 + 描述 + 参数 schema) 推进每一次请求。装的 skill / MCP 越多,每轮对话要付的 token 越大、模型挑工具的噪声越高。

dsh-lazy-toolbox 做三件事:

  1. 搬走:安装时把本机 skill 目录、profile 里的 mcp-client 配置整体搬进插件库,DSH 因此不再把它们 放进全局工具定义。卸载时按原文原位置还原。
  2. 留目录:系统提示里只留一份清单 —— skill 与 mcp 分开列,每条一句话用途,不含任何调用参数。
  3. 热加载:模型需要时调用 toolbox,把那一项的定义注册进当前会话(agent.ctx 作用域,只影响这一个 对话),用完 unload 撤掉,下一轮请求立刻变小。
安装前                         安装后
┌──────────────────┐          ┌──────────────────┐
│ tool: mcp__gh__x │          │ tool: toolbox    │  ← 常驻,唯一的入口
│ tool: mcp__gh__y │          │ prompt: 目录     │  ← skill/mcp 各一行,无 schema
│ tool: mcp__db__q │          └──────────────────┘
│ ... 40 个         │                    │ load
└──────────────────┘                    ▼
                              ┌──────────────────┐
                              │ 本会话工具表       │  ← 只有此刻需要的定义
                              └──────────────────┘

快速开始

从图形界面装(最省事)

插件 → 添加插件,填本地目录路径(解压出来的 dsh-lazy-toolbox 目录)或包名都行。 包里有 dsh.bundle 声明(cordis.patch.yml),DSH 会自己写好依赖、bundles 和配置行,重启生效。

0.7.0 及更早没有这个声明,安装器会报 这个包没有声明组合包,无法作为插件安装:dsh-lazy-toolbox declares no dsh.bundle,换 0.7.1+ 即可。

从本地目录接入(开发 / 自用)

# 1) 把插件接到 profile(bundles + insert 行 + node_modules 链接,幂等)
dsh-lazy-toolbox wire-profile

# 2) 重启 dsh,插件首次启动会自动迁移本机 skill 与 MCP 配置

也可以让 dsh 的插件管理器装它,或直接写进 profile:

// $DSH_HOME/profiles/<profile>/package.json
{
  "dependencies": { "dsh-lazy-toolbox": "link:D:/path/to/dsh-lazy-toolbox" },
  "dsh": { "profile": { "bundles": ["@deepseek-ai/dsh-base", "...", "dsh-lazy-toolbox"] } }
}
# $DSH_HOME/profiles/<profile>/cordis.patch.yml 末尾
- insert:
    - id: lazy-toolbox
      name: dsh-lazy-toolbox
      inject: [systemPrompt, tools]
      config:
        autoImport: true
        autoUnloadOnTurnEnd: true

从 npm 安装

npm i -g dsh-lazy-toolbox
dsh-lazy-toolbox wire-profile

模型看到什么

系统提示里多出这样一段(节选):

## 懒工具箱(skill / MCP 按需加载)
下面这些能力**已经安装但不在当前工具列表里**(所以你看不到它们的参数)。需要时调用 `toolbox`:
- `toolbox { action: "load", ids: ["skill:<名字>" 或 "mcp:<服务>" 或 "mcp:<服务>:<工具>"] }` 把定义注入本会话;
- `toolbox { action: "unload", ids: [...] }`(或 `all: true`)把定义撤掉,减少后续请求体积;
- `toolbox { action: "list", ids: [...] }` 展开某个 MCP 服务的工具清单;
- 用户要求安装新的 skill / MCP 时用 `action: "add_skill" / "add_mcp"`,并给一句话 `summary`。

skills:
- skill:release-notes — 按提交记录生成发版说明 [已加载]
- skill:sql-review — SQL 变更评审清单

mcp:
- mcp:github — GitHub 仓库、Issue 与 PR 操作,38 个工具
- mcp:postgres — 只读 SQL 查询

load 之后该会话的工具表里才会出现 mcp__github__*;unload(或回合结束)后消失。 目录里的 [已加载] 是给模型的状态提示,不影响其他会话。

新会话不会被蒙在鼓里

光有目录还不够:模型容易扫一眼就当没看见,直接凭记忆答。所以插件另外注册了一个 运行时上下文提醒(systemPrompt.context),每个会话一开始就出现在上下文快照里:

懒工具箱:本机还装了 2 个 skill、1 个 MCP 服务,它们不在工具定义里,
详情见系统提示里的目录(每条一句话用途)。只要当前任务和某条用途对得上,
先调用 `toolbox { action: "load", ids: ["skill:<名字>"] }`(MCP 用 "mcp:<服务>")
把定义注入本会话,再动手;不要因为工具列表里没有、就跳过它或凭记忆作答。
用完用 `toolbox { action: "unload", ... }` 撤掉。
  • 每个会话都有,加载过东西之后这条文本变成空串,DSH 会把它从上下文快照里过滤掉,不占 token;
  • 因为默认 autoUnloadOnTurnEnd,每个新的用户回合会重新提醒一次 —— 正是「别的对话不知道有这个插件」的对症药;
  • 库里为空(没装任何 skill / MCP)时不输出,不会无中生有。

toolbox 的全部动作

action 作用
status 插件库概况、本会话已加载项、待还原记录
list 目录(skill / mcp 分列);ids 指定某个 mcp:<服务> 时展开它的工具清单
load 把 ids 里的项目注入当前会话;skill 直接给出正文
unload 撤掉 ids(或 all: true)注入的定义
add_skill 从本地路径或 http(s) URL 导入 skill(也支持 git 仓库)
add_mcp 登记并先连一次验证 MCP,成功后记录工具数;失败自动回滚
remove 从插件库删除 skill 文件 / MCP 配置(ids:["skill:x"],也接受单个 id)
enable / disable 打开 / 关闭某个 skill 或 MCP 服务(等价于设置页里的那个开关)
skill 按需取某个 skill 的某一节正文(大纲模式下的「看详情」;section: "all" 整篇)
mode 设置某条目加载方式:mode: "lazy"(懒加载)或 "skip"(跳过懒加载)
update 云更新:step: "check" 查新版本、"download" 下载解压、"install" 覆盖安装(装完需重启)
code 内置代码编辑器:step: "read" / "validate" / "write" / "patch" / "revert",写前先校验、不通过不落盘(见 docs/editor.md)
discover 扫一遍已安装插件自带的 skill / MCP,发现就接管(dry_run: true 只看不写)

设置页:懒加载 / 工具 / 关于

设置 → 懒工具箱 分三个标签:

  • 懒加载:三类开关(插件工具 / skill / MCP)与每条的加载方式;
  • 工具:扫描插件内置资产、撤掉本会话注入、内置代码编辑器(读写文件不经过终端, 写前先做语法校验,错了直接给行号列号);
  • 关于:版本、仓库、当前托管数量、云更新。

进 DSH 后会自动查一次新版本(约 4 秒),有新版就弹窗:本次不提醒 / 今天不提醒 / 更新。

外部工具链(JDK / Ghidra / rea-agents / JADX / IDA)的检测与一键安装已经拆成独立插件 dsh-toolchain:那边有独立的 toolchain 工具与 设置 → 工具链 页面。本插件的 toolbox 不再提供 requirements / install。

插件工具:把别的插件的工具也变懒

有的插件一次注册十几个工具(比如内存工作台的 17 个 mem_*),聊什么都没它们的份,却一直占着 工具定义。现在它们可以把工具交给懒工具箱托管:

## 插件工具(别的插件交出来托管的)
- group:memstudio — 内存工作台:附加进程 / 读写搜索 / 断点 / 指针链 / 冻结〔插件 dsh-memstudio〕,17 个工具:mem_targets, mem_apps, …

启动时它一个工具都不注册;要用时 toolbox { action: "load", ids: ["group:memstudio"] } (或只挑一个 group:memstudio:mem_read),用完 unload。设置页里「插件工具」单独一栏, 同样可选 懒加载 / 跳过。

插件侧接入就两行(全局符号桥,不挑安装路径):

const bridge = globalThis[Symbol.for("dsh-lazy-toolbox.bridge")]
if (bridge) bridge.provideToolGroup({ id: "memstudio", summary: "一句话用途", tools })
else for (const t of tools) ctx.tools.register(t)   // 没装懒工具箱就照旧常驻

细节见 docs/plugin-tools.md。

云更新:查新版本 → 看更新内容 → 下载 → 安装 → 提示重启

设置页第一张卡片是「云更新」,它问的是本仓库的 GitHub Release:

当前 v0.5.3 · 有新版本 0.6.0        [检查更新] [下载 v0.6.0] [安装 v0.6.0]
(下面是这个 Release 的更新内容,可滚动)
  • 检查:GET /repos/<updateRepo>/releases/latest,结果落盘在 <数据目录>/update-state.json; 启动时先显示缓存,再延迟 4 秒后台查一次,不阻塞启动;
  • 下载:发布包 zip 下到 <数据目录>/updates/ 并解压好,大小对不上直接报错;
  • 安装:先把现版本文件备份到 updates/backup-<版本>-<时间戳>/,再覆盖插件本体; node_modules / .git / 数据目录不碰;
  • 装完只提示重启:红字「请关掉 DSH 再打开」——插件不会自己重启进程。

三条路径都行:设置页按钮、命令行 dsh-lazy-toolbox update check|download|install、 AI 侧 toolbox { action: "update", step: "check" }。细节见 docs/update.md。

设置 → 懒工具箱:插件工具 / skill / MCP 三类开关

设置页第一屏就是全部 skill 与全部 MCP(分两组),每条一行:名字、一句话用途、 工具数 / 来源,右边一个开关。关掉之后:

  • 它不再出现在系统提示的目录里,AI 也就不会再被告知它存在;
  • toolbox action="load" 会明确拒绝(「在设置页里是关闭状态」),不会偷偷加载;
  • 文件与配置都还在(store.json 里记成 disabled.skills / disabled.mcp),点一下就能回来。

适合「装了很多但这一轮用不到」的场景:不用删,也不会占目录与提示的 token。

// store.json 片段
"disabled": { "skills": ["reverse-engineer-anything-cn"], "mcp": [] }

skill 只注入大纲,正文按需取

一个 3 万 token 的 skill(比如「漫剧制作流程」),load 时不整篇塞进上下文,只给:

  • 一句话用途(本来就在目录里);
  • 大纲:每个分支(小节)一行,带一句人话概括;
  • 取节方式:toolbox { action: "skill", id: "skill:ds", section: "2" } 只回「分镜」那一节, section: "all" 才整篇。

没有多一个工具定义——取节走的是常驻的 toolbox。细节见 docs/skill-index.md。 要整篇:单个 skill 在 store.json 写 loadMode: "full",或全局 skillInjectMode: full。

加载方式:懒加载 / 跳过

设置页每个条目右边是两段式选择器:懒加载(进目录、按需注入)或 跳过 (不做懒加载:不进目录、load 拒绝,文件与配置都还在)。AI 侧等价动作是 toolbox { action: "mode", ids: [...], mode: "skip" },命令行是 dsh-lazy-toolbox mode <id> skip。

「不做懒加载」名单(破甲类插件)

有些插件自己就往系统提示词里 hook 一大坨提示词,或自带「教怎么绕过限制」的 skill。 这类插件的内置资产不接管、不进目录(避免和插件自己的注入打架、也避免一句话骗过 AI):

  • 内置名单:dsh-purge、dsh-jailbreak、dsh-uncensored、dsh-prompt-inject 等;
  • 内置模式:包名/条目名含 jailbreak / 破甲 / 越狱 / uncensor / nsfw / prompt-inject 等;
  • 追加自己的:neverLazyExtra: ["my-plugin"];关掉内置名单:neverLazyBuiltin: false;
  • 只影响懒工具箱的接管,不删任何文件。细节见 docs/never-lazy.md。

插件自带的 skill / MCP:自动接管

有些插件自己就带能力(skills/ 目录、mcp.json,或 package.json 里的 dsh.skills / dsh.mcp)。 0.5.2 起这些会被自动登记进插件库,不用你一个个 add_skill:

  • 扫描时机:插件启动时 + 每次扫描(内容没变时按指纹跳过,不重复读盘);
  • 原地引用:不复制文件,插件升级后 load 到的就是新内容;
  • 幂等 + 卸载回收:插件没了,条目下一轮自动删掉;你 disable / remove 过的不再复活;
  • 不想被接管:package.json 里写 "dsh": { "autoDetect": false };
  • 想立刻扫:node bin/cli.mjs discover [--dry-run],或让 AI 调 toolbox { action: "discover" }。

支持的全部写法、配置字段与例子见 docs/plugin-assets.md, 插件骨架见 examples/plugin-with-skill/。

面板与 AI 看到的是同一份事实:GET /dsh-lazy-toolbox/api 返回的 inventory, 改开关走 POST {"op":"toggle","id":"skill:<名字>","enabled":false}。

工具链面板已拆走

外部引擎(JDK / Ghidra / rea-agents / JADX / IDA)的检测、一键安装与 env 同步都在独立插件 dsh-toolchain 里:它有独立设置页 设置 → 工具链(order 55,排在懒工具箱之前)和常驻工具 toolchain。本插件不再注册工具链面板,也不再调用 applyEnv()。

拆分时状态文件 $DSH_HOME/lazy-toolbox/requirements.json 原样保留:dsh-toolchain 首次加载时 会读它并写进自己的 $DSH_HOME/toolchain/requirements.json,所以拆完面板上立刻是「已安装」, 不需要重装几百 MB 的引擎。MCP 服务注册表 store.json 仍由本插件维护,dsh-toolchain 只往里写 REA 条目的 env。

写操作失败时返回一段可读结果(# add_skill 失败 …),不会把异常直接抛给模型。

add_skill / add_mcp 都会要求一句话 summary —— 这就是目录里那一行用途。

生命周期

时机 行为
插件启动 迁移 profile 里的 mcp-client 条目、$DSH_HOME/skills、~/.agents/skills(可选项目目录)
每个 pre-step 节流扫描:AI 之后下载进标准目录的 skill、新加的 mcp-client 行,自动搬进来并补一句话用途
回合结束 默认自动卸载本会话注入的定义(autoUnloadOnTurnEnd),把请求体积还回去
agent 销毁 撤销该 agent 的全部注入,关闭空闲 MCP 连接
插件卸载 preuninstall 先还原全部 skill 与 MCP 行,再摘 profile 接线

还原语义:MCP 行保存的是原文整块 + 原始行号,还原时按行号写回;skill 目录整体移回原位。 文件被外部改过时会先写 <file>.lazy-toolbox-backup 再动。冲突(同名已存在)一律跳过并报告,从不覆盖。

配置

config:
  dataDir: ""                  # 插件库位置,默认 $DSH_HOME/lazy-toolbox
  autoMigrate: true            # 启动时迁移
  autoImport: true             # 运行期自动导入新 skill / 新 mcp 行
  autoImportProjectSkills: false  # 是否也接管项目里的 .dsh/skills
  scanIntervalMs: 5000         # 自动导入的节流间隔
  autoUnloadOnTurnEnd: true    # 回合结束自动卸载
  mcpIdleMs: 60000             # MCP 连接空闲多久关掉
  maxCatalogItems: 200         # 目录最多列多少条
  autoDetectPluginAssets: true # 自动接管插件自带的 skill / MCP
  updateRepo: anyi-520/dsh-lazy-toolbox # 云更新查的仓库(可换 fork)
  updateCheck: true            # 启动时自动检查新版本
  updateCheckHours: 6          # 隔多久再查
  skillInjectMode: index       # skill 注入方式:index(只给大纲)| full(整篇)
  skillSectionMaxChars: 60000  # action=skill 单次回传上限
  neverLazyBuiltin: true       # 「不做懒加载」内置名单
  neverLazyExtra: []           # 额外名单(包名子串)
  logEnabled: true             # 本地运行日志(只写文件,设置页不显示)
  logDir: ""                   # 日志目录,默认 $DSH_HOME/lazy-toolbox/logs
  logLevel: info               # debug | info | warn | error
  logMaxBytes: 2097152         # 单个日志文件上限,超了就分卷
  logRetentionDays: 14         # 保留天数,0 = 不清理

运行日志(本地)

插件把自己的功能调用和错误写成 JSONL,只落本地文件,设置页里看不到任何东西:

$DSH_HOME/lazy-toolbox/logs/plugin-YYYYMMDD.log        # 超 2 MB 分卷成 .1 .2 …

一行一条,字段固定 ts / level / event / pid,其余按事件附带:

event 记录内容
plugin.start / plugin.stop 版本、数据目录、生效配置、运行时长
toolbox.call 每次 toolbox 调用:action、ids、ok、ms,失败带 error.stack
toolbox.failed guard() 兜住的写操作失败(add/remove/enable/disable)
inject.load / inject.unload 逐条注入/移除结果(成功与否、注入了几个工具定义)
mcp.connect / mcp.close 连的哪个 MCP、传输方式、命令;连不上带堆栈
api.request 设置页的 HTTP 调用:方法、op、id、状态码、耗时
scan.failed / migrate.* 自动导入与迁移的结果/失败

写日志失败会被静默吞掉(日志不能拖垮插件),超长字符串截断到 2000 字符,按天分文件、超限分卷、按 logRetentionDays 自动清理。看日志:

dsh-lazy-toolbox logs                 # 最近 100 条,人类可读
dsh-lazy-toolbox logs --lines 20 --raw   # 原样 JSONL,方便 grep / jq
dsh-lazy-toolbox logs --path           # 只打印今天的文件路径

CLI

dsh-lazy-toolbox status            # 插件库概况
dsh-lazy-toolbox list              # 目录
dsh-lazy-toolbox migrate [--dry-run] [--project]
dsh-lazy-toolbox restore [--skills-only|--mcp-only]
dsh-lazy-toolbox add-skill <path> [--name n] [--summary "一句话"]
dsh-lazy-toolbox add-mcp <name> --command "npx" --args "-y some-mcp" [--verify]
dsh-lazy-toolbox remove skill:<name>|mcp:<server>
dsh-lazy-toolbox enable skill:<name>|mcp:<server>     # 打开(等同设置页的开关)
dsh-lazy-toolbox disable skill:<name>|mcp:<server>    # 关闭:不进目录、不能 load,文件还在
dsh-lazy-toolbox wire-profile [--profile <dir>]
dsh-lazy-toolbox unwire-profile [--profile <dir>]
dsh-lazy-toolbox discover [--dry-run]             # 扫插件自带的 skill / MCP
dsh-lazy-toolbox update check|download|install     # 云更新三步
dsh-lazy-toolbox mode skill:<名字> lazy|skip       # 加载方式:懒加载 / 跳过
dsh-lazy-toolbox skill <名字> [--section <id>|all]  # 看大纲 / 取某一节
dsh-lazy-toolbox logs [--lines N] [--raw] [--path]   # 本地运行日志
dsh-lazy-toolbox doctor            # 自检:数据目录、存储、链接状态

--data-dir 可指向别的插件库(测试用)。

为什么能做到「只影响当前会话」

DSH 的工具注册表按 agent 作用域分层(@deepseek-ai/dsh-scope)。agent.ctx.tools.register(def) 注册的定义只对该 agent 可见,返回值是精确的撤销函数;agent 销毁时作用域整体回收。 本插件不碰全局注册表,只往当前 agent 的作用域里加/减,所以:

  • 会话 A 加载的 MCP 工具,会话 B 看不到;
  • 卸载只影响发起卸载的会话;
  • 进程重启后一切回到「只有目录」。

设计取舍

  • 零运行时依赖:MCP 客户端(stdio + streamable-http)、YAML 子集解析、schema 收敛都在仓库内实现。 不 import 任何 @deepseek-ai/*,避免 profile 的 .pnpm 提升与 asar 解析问题;与 DSH 的全部交互都走 ctx 服务。
  • schema 有损降级:DSH 只接受 type/oneOf/properties/required/additionalProperties/items/enum/const 加注解,MCP 常见的 $ref、anyOf、format 会被拒绝。这里统一收敛,宁可丢约束也不让整个服务加载失败。
  • 卸载是卸载:不删用户文件,只搬走再搬回。

兼容性

  • DSH Desktop 0.2.1-rc.x(inject: [systemPrompt, tools],tools.register 需要 output.schema + render)
  • Node.js >= 20

开发

node --test --test-force-exit "test/*.test.mjs"   # 21 个测试
node bin/cli.mjs doctor

测试里有一个真实的 stdio MCP 样本(test/fixtures/fake-mcp-server.mjs),MCP 客户端、注入、卸载、 回合结束清理、schema 收敛都是真跑的,不打桩。

目录结构

lib/
  index.js       插件入口:注册 toolbox、目录 section、迁移与钩子
  tools.js       常驻工具 toolbox 的动作实现
  inject.js      按会话注入/卸载
  mcp.js         MCP 连接池、工具名规范化、定义包装
  mcp-client.js  stdio / streamable-http 传输
  skills.js      skill 发现、搬入、加载、还原
  migrate.js     迁移与还原(MCP 行 + skill 目录)
  profile.js     profile / cordis.patch.yml 的文本级读写
  schema.js      上游 JSON Schema → DSH 子集
  store.js       插件库(store.json + skills/)
  discover.js    插件内置 skill / MCP 的发现与接管(原地引用)
  outline.js     skill 大纲:骨架 + 每节一句话 + 按需取节
  denylist.js    「不做懒加载」名单(破甲类插件)
  update.js      云更新:检查 / 下载 / 覆盖安装(先备份,装完提示重启)
  tool-groups.js 插件工具托管:别的插件把工具交出来,目录里只留一行 + 全局符号桥
  log.js         本地运行日志(JSONL,按天分卷 + 保留期清理)
  util.js        文件、路径、frontmatter 工具
bin/cli.mjs      命令行
scripts/         postinstall / preuninstall 钩子

License

MIT,见 LICENSE。

—/ 5

No ratings yet

Verified DSH bundle

Commit 8dbf8d5600aa

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout