Hermes-style tool search & slimming: progressive disclosure, semantic search, describe, and call long-tail tools on demand while core tools stay eager.
Install
# from npm (prebuilt)
dsh plugin --profile web add dsh-tool-search
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:Letter2025/dsh-tool-search
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
Tool search & slimming for DeepSeek Harness (DSH): Hermes-style progressive disclosure. When your tool catalog gets large (many MCP servers or plugin tools), every tool's JSON schema is injected into the model context on every turn — wasting tokens on tools the task never uses. This plugin collapses the long tail behind three bridge tools and lets the model discover and load them on demand through a configured rerank model.
- Core tools stay eager — file/shell/essential tools are always directly visible.
- Bridge tools —
tool_search,tool_describe,tool_callreplace the deferred schemas. - Tiered disclosure — the visible listing shrinks automatically as the catalog grows.
- Conversational setup — the bundled
tool-slimmer-setupskill groups your tools by talking to you, and guides configuring the rerank model. - User or project config — groups/matcher live in
~/.dsh/dsh-tool-search.json(global) or<workspace>/.dsh/dsh-tool-search.json(per project), your choice.
Install
dsh plugin --profile web add dsh-tool-search
How it works
Inspired by Hermes Agent's Tool Search. On every turn, the plugin's system-prompt/assemble listener computes the tier and replaces the model-visible tools:
| Tier | Condition | Model sees |
|---|---|---|
| 0 | Small catalog / nothing deferrable | Every tool, bridge absent |
| 1 | Grouped manifest fits the budget | Bridge + ## group name/description listing |
| 2 | Only names fit | Bridge + names-only listing |
| 3 | Even names overflow | Bridge + one line per group (name: count) |
Budget = min(thresholdPct% × contextWindow, listingMaxTokens), recomputed every turn. The manifest rides a runtime context, so it survives complete-prompt composition.
Dynamic injection: searching, describing, or calling a deferred tool warms it into the session's visible set (LRU-bounded by maxWarmTools), so its full schema is injected into the context for later turns — the model pulls tools into context on demand instead of keeping everything.
When tool_call runs, the plugin executes the real tool by name through ctx.tools.execute, so approvals, guards, and session events all reference the underlying tool — never the bridge. tool_search ranks with the configured rerank matcher and falls back to keyword matching (exact name > name tokens > description tokens) when no matcher is configured or the rerank call fails.
Setup (conversational)
Ask your agent:
帮我配置 dsh-tool-search 的工具分组
The tool-slimmer-setup skill will read the catalog, propose groups, confirm with you, ask whether the config should be global or per-project, and guide you through configuring the rerank matcher (required for tool_search and preload).
Configuration
Static tuning lives in your profile cordis.patch.yml (restart to change):
- id: tool-search
config:
enabled: auto # auto | on | off
thresholdPct: 5 # listing budget as % of context window
listingMaxTokens: 4000
configScope: auto # user | project | auto (project file wins when present)
core: [todo_write] # extra always-eager tools
maxWarmTools: 8 # LRU cap for dynamically injected tools
Tool groups, the matcher, and preload live in the runtime file (~/.dsh/dsh-tool-search.json for user scope):
{
"version": 1,
"scope": "user",
"groups": [
{ "name": "git", "tools": ["git_status", "git_diff"] },
{ "name": "mcp-github", "prefixes": ["mcp_github_"] }
],
"matcher": {
"endpoint": "https://dashscope.aliyuncs.com/compatible-mode/v1/rerank",
"apiKey": "sk-...",
"model": "qwen3-reranker",
"topN": 20
},
"preload": { "enabled": false, "topK": 5 },
"core": ["read_file", "write_file"]
}
groups: exact tool names and/or name prefixes; a tool belongs to the first matching group.matcher: an OpenAI-compatible/v1/rerankendpoint — the only matcher type.tool_searchreturns setup guidance until one is configured.preload: optional; when enabled (with a matcher), the session's first turn semantically preloads the top-K matching tools into the eager set.- The file is watched by mtime and hot-reloads; changes take effect on the next turn.
Bridge tools
| Tool | Purpose |
|---|---|
tool_search(query, limit?) |
Search the deferred catalog (rerank, keyword fallback) and return ranked {name, description, group} matches; matches are injected into the visible context |
tool_describe(name) |
Load the full schema of one deferred tool; the tool is injected into the visible context |
tool_call(name, arguments) |
Invoke a deferred tool by real name; approvals/guards/events use the real tool; the tool is injected into the visible context |
Design notes & pitfalls
- Slimming only rewrites the model-visible surface (
system-prompt/assemble); the registry stays complete, so deferred tools remain executable. - The bridge, the two setup tools, and
skillare always eager and never defer themselves. - PTC (
run_code) scopes keep the transport, not the declarations. Such a scope's wire table is collapsed torun_codealone, with every declaration moved into thetools:sdkprompt section — the tokens this plugin exists to slim. The plugin leaves that table alone (replacing it deletedrun_codefrom the prompt) and re-renders the section instead: the eager core plus the bridge tools, with the deferred catalog injected as a runtime context under arun_code-aware header. The section's generator is read from its own text (interface ToolArgsMap= TypeScript,TypedDict= Python); text from an unknown generator is left untouched rather than rewritten in the wrong language. Abothscope keepsrun_codeeager (the bridge refuses to invoke it by name) and gets the same declaration rewrite; native sessions are unaffected. - dsh 0.2.1-rc.3 moved two seams under this plugin. The PTC generators now live in
@deepseek-ai/dsh-tool-executor-ptcand are no longer exported by@deepseek-ai/dsh-tools, so they are resolved with a dynamic import at run time — newest package first,dsh-toolsas the fallback, and a harness carrying neither loads the plugin with the section rewrite disabled instead of failing the import graph. That release also hands the waterfall onlysections/contexts/variablesand appends its own tool table afterwards, so an assembly without a table of its own is returned untouched: on such a host the plugin ships the declaration rewrite and the injected catalog, and the table slimming applies to earlier releases. On 0.2.1-rc.4, 0.2.2-alpha.2 and 0.2.2-rc.1 the same split is total: native schemas belong to the host's tool executor, and the step'sResolvedToolSet(context.toolSet) is threaded into every catalog and declaration lookup so tools a step did not admit are never disclosed. 0.2.2-rc.1 changed none of the seams this plugin uses, so v0.1.10 is a manifest-only release. - Keyword matching is only a fallback: rerank is the primary ranking; without a matcher or on rerank failure, search degrades to keyword matching (exact name > name tokens > description tokens) and never errors.
- See DESIGN.md for the full architecture.
Links
License
MIT
Links
More in this category
DayuanJiang/next-ai-draw-io#dsh-plugin★ 36802
Draws draw.io diagrams in DeepSeek Harness through the next-ai-drawio MCP server: the link in the reply opens a live preview in the right sidebar, you can edit the diagram by hand, and each AI change is a version you can restore. Node.js 18+; npx downloads the MCP server on first use.
Tencent/WeKnora#dsh-weknora★ 36302
Four read-only tools over a WeKnora knowledge base: list knowledge bases, hybrid passage search, reassemble one document's chunks in order, and WeKnora's own cited RAG or ReAct-agent answer with a resumable session id.
superdesigndev/treg★ 7042
Tool catalog for agents: search ~2,600 external endpoints (SEO and SERP, backlinks, social, people and company enrichment, ad libraries, scraping) by the task you want done, read each one's parameters and per-call price, then call it with the credential injected server-side. Ships the skill plus an MCP row that stays disabled until TREG_TOKEN is set.
T-Auto/dsh-ops★ 5074
Provides bash, PowerShell 7, and high-performance Rust tools for DSH on Windows to reduce token usage.
sixtysevenlf/dsh-blender-plugin★ 1575
Lets a DSH agent drive a local Blender over a direct TCP channel with 15 tools: viewport frames, custom-angle renders, Python execution, headless and job offload, transaction rollback, and a planning layer of 228 ops across 34 families.
TencentCloudBase/CloudBase-AI-Toolkit#dsh-plugin★ 1155
Tencent CloudBase backend for DeepSeek Harness — scaffold and deploy full-stack apps from chat, render query results as table cards with paging, sorting and CSV export, preview a deployment on its domain, and call the CloudBase MCP toolset (`mcp__cloudbase__*`) with device-code login.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.