dsh-capability-menu

One unified capability management surface for DeepSeek Harness: control the exposure level (context footprint) and execution of Tools and Skills

npm version downloads GitHub stars DeepSeek Harness 0.1.5-rc.2 featured in awesome-dsh-plugin CI

简体中文 · English


## Table of Contents - [Capability Overview](#capability-overview) - [Quick Install](#quick-install) - [Exposure Policy](#exposure-policy) - [Configuration](#configuration) --- ## Capability Overview dsh-capability-menu is a Cordis plugin for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness). It builds a unified capability catalog (`ctx.capability`) over a large number of tools / skills (MCP tools and harness-native built-in tools) and manages their **exposure level and execution** in three tiers — **Resident / On-demand / Disabled** — so you can adjust the agent's capability boundary at any time, keep a flood of tools/skills out of a single request, and save tokens and context. Changes apply immediately without restarting, and the plugin composes into the Harness runtime purely through the Cordis plugin mechanism — no upstream source is modified. **Without this plugin (policy) mounted, everything stays visible as before; mounted with no rules at all, every capability defaults to Resident.** ### Capability Model *Capability* is the umbrella concept introduced by this plugin: a Tool and a Skill are different *kinds* of capability. | kind | provides to the agent | action | notes | | --- | --- | --- | --- | | `tool` | executes an action (an MCP tool or a harness-native built-in tool) | `execute` | indexed by `ctx.tools` | | `skill` | the method / flow / knowledge for a class of tasks | `load` | indexed by `ctx.skills` | The model gets two meta tools: | tool | role | corresponding entry | | --- | --- | --- | | `meta_search` | search the capability catalog (Tool / Skill), list/detail dual mode | `@daweifu/capability-menu/search` | | `meta_invoke` | unified execution surface: really executes Tools (full `ctx.tools` pipeline) + loads Skills | `@daweifu/capability-menu/invoke` | ### Capability Management

Tools tab Skills tab

Policy & catalog · Policy (effective) Policy & catalog · On-demand catalog

Once installed, a Capability Management tab appears under Settings → General Settings (between "Model" and "Plugins"). It is where you view and adjust a capability's exposure tier; changes apply immediately, no restart needed. | What you want to do | Where | | --- | --- | | Change a tier | Click the dot on a capability row, or a tier count at the top to switch the whole group | | Register an MCP server / skill directory | Register capability, top right | | Edit or remove a registered entry | Edit on an MCP server's group header (Tools) or a skill row (Skills) | | See the effective policy and the On-demand catalog | Policy & catalog, on the header's description row | | The list is stale (you changed a source outside dsh) | Refresh, at the top | **The tier is that dot**: filled = Resident (the model calls it directly), half-filled ring = On-demand (reached through `meta_search` → `meta_invoke`), ring with a slash (a no-entry sign) = Disabled. If a higher-priority rule (a wildcard, say) overrides it, the UI reports that the classification did not apply. **How the page is laid out**: the Tools tab groups by server and folds — MCP tools under their own server, harness-native tools together under the built-in group; the Skills tab splits into "Global skills" / "Project skills". Click a capability row for its model-facing definition, a skill row to expand its directory tree, and a file to preview it. **Three behaviours worth knowing**: - **A tier click does not hit disk immediately**: it changes memory first (so it feels instant) and is written back to `patchFile` (the home layer's `~/.dsh/cordis.patch.yml` by default) once you stop for ~1.5s — writing that file makes dsh hot-reload this plugin, so it cannot happen on every click. - **Registering writes files**: an MCP server goes into the same patch file (an `@deepseek-ai/dsh-mcp-client` row, mounted natively by dsh — this plugin never manages the connection), and a skill directory becomes a symlink under the skill root. Credentials such as headers are stored there in **plain text**. - **Some skill rows offer Adopt rather than Edit**: those are skills in user-level roots such as `~/.agents/skills` / `customSkillDirs`. The confirmation names the directory it currently lives in, and confirming links it into `~/.dsh/skills/` with the **content untouched**; rows that cannot be adopted show their source instead. What each form field means, how visible a global versus a project skill is, what a removal actually costs, and how `SKILL.md` is validated are all stated where you act on them — no need to repeat them here. ## Quick Install Prerequisites: Node.js and the dsh CLI installed (`dsh plugin` forwards to pnpm internally). ### Install from npm (recommended) A single package ships both the server-side plugin and the front-end Capability Management tab; once installed it shows up under Settings → General Settings: ```sh dsh plugin --profile web add @daweifu/capability-menu ``` ### Install from source ```sh git clone https://github.com/PKUfudawei/dsh-capability-menu.git cd dsh-capability-menu pnpm install # the prepare script builds lib/ (server) and lib/client.js (front-end) dsh plugin --profile web add ./dsh-capability-menu ``` ### Verify the install ```sh dsh --profile web --dump-config | grep -E 'capability-menu' ``` ``` # == @daweifu/capability-menu - id: capability-menu-registry name: '@daweifu/capability-menu/registry' - id: capability-menu-search name: '@daweifu/capability-menu/search' - id: capability-menu-invoke name: '@daweifu/capability-menu/invoke' - id: capability-menu-policy name: '@daweifu/capability-menu/policy' - id: capability-menu name: '@daweifu/capability-menu' ``` ### Uninstall ```sh dsh plugin --profile web remove @daweifu/capability-menu ``` ## Exposure Policy All capabilities (Tool and Skill) fall into three tiers by their **exposure level** (what the model sees in the context) and their **execution mode**: ### Tools / Skills three-tier exposure and execution | tier | capability | exposure (model view) | discovery | execution | | --- | --- | --- | --- | --- | | **Resident** | tool | full schema in `assembly.tools` → the model's `tools` request payload, visible at every step | none (already resident) | model calls it directly; at runtime it goes through the full `ctx.tools` pipeline | | | skill | name + description in the `` catalog (body not in the catalog) | none (already resident) | the `skill` tool loads the body on demand (on-demand loading) | | **On-demand** | tool | not in the payload (zero context cost) | `meta_search` list / `grep` the materialized catalog YAML (`catalogFile`) | executed by `meta_invoke` (via `ctx.tools.execute`, full pipeline); or fetch the schema through detail and call it directly | | | skill | not in the `` catalog | `meta_search`, or `grep` the materialized catalog YAML (`catalogFile`) | `meta_invoke` loads the SKILL.md body (via `ctx.skills`) | | **Disabled** | tool | not in the payload | not returned by `meta_search`, not written to the catalog YAML | refused by `meta_invoke`; hallucinated direct calls are also hard-rejected in `tools/pre-execute` | | | skill | not in the `` catalog | not returned by `meta_search`, not written to the catalog YAML | refused by `meta_invoke`; the `skill` tool is hard-rejected in `tools/pre-execute` | > **Scope & reserved tools**: > - The tool tiers cover both `mcp__` cataloged tools and harness-native built-in tools (native tools are grouped under the reserved `built-in` server and are managed in all three tiers exactly like MCP tools). **Do not name a real MCP server `built-in`.** > - `meta_search`/`meta_invoke` are this plugin's control plane: always Resident, cannot be disabled (a rule that disables one fails at startup). `run_code` is the reserved Code Mode transport: it never enters the catalog, does not appear in Capability Management, and should not get tier rules. > - **Keep high-frequency core tools Resident**: an On-demand built-in tool leaves the model's resident view and needs a `meta_search` → `meta_invoke` two-hop call. ## Configuration Rules are declared under the `config` of this plugin's `capability-menu-policy` entry — by default in the home layer's `~/.dsh/cordis.patch.yml` (`$DSH_HOME` wins), and a profile's `cordis.patch.yml` can also amend it with an id-targeted override patch (the outer `- insert:` / `id` / `name` is Cordis patch boilerplate and has nothing to do with the rules): ```yaml config: tools: resident: - execute_cmd - get_session_context - search_kb - 'mcp__gongfeng__*' # wildcard: everything under this server is resident on-demand: - 'mcp__*' # wildcard fallback - 'server:km:*' # bulk on-demand by server prefix disabled: - 'mcp__secret__*' # disabled outranks everything, even resident skills: resident: - debugging - coding on-demand: - legacy_skill # explicit on-demand (unlisted skills default to resident) disabled: - forbidden_skill metaTools: - meta_search # always resident; cannot be disabled - meta_invoke ``` > Config keys are the tier words themselves: `resident` (常驻) / `on-demand` (按需) / `disabled` (禁用). ### All configuration options | Option | Entry | Default | Description | | --- | --- | --- | --- | | `tools` / `skills` / `metaTools` | `capability-menu-policy` | see above | Tier rules; UI changes are written back to this entry's `config` after the debounce | | `catalogFile` | `capability-menu-registry` | `~/.dsh/capability-catalog.yaml` | Materialized on-demand catalog path; empty disables it | | `refreshDebounceMs` | `capability-menu-registry` | `200` | Debounce window (ms) for change-event rebuilds; `0` disables debouncing | | `patchFile` | `capability-menu-policy` | `~/.dsh/cordis.patch.yml` (`$DSH_HOME` wins) | Patch file that MCP server registration writes to | | `skillsDir` | `capability-menu-policy` | `~/.dsh/skills` | Skill root used by skill directory registration | | `persistDebounceMs` | `capability-menu-policy` | `1500` | Debounce window (ms) before a clicked tier change is written back to the patch file | **Rule priority** (first match wins; within one tier, an exact rule beats a wildcard): | priority | rule | example | effect | | --- | --- | --- | --- | | 1 | `disabled` exact | `disabled: [forbidden_skill]` | hardest deny, overrides everything | | 2 | `disabled` wildcard | `disabled: ['mcp__secret__*']` | block a whole group | | 3 | `resident` exact | `resident: [bash]` | keep one capability resident | | 4 | `on-demand` exact | `on-demand: [legacy_skill]` | one capability on-demand (what a Capability Management click writes) | | 5 | `resident` wildcard | `resident: ['mcp__gongfeng__*']` | keep a whole group resident | | 6 | `on-demand` wildcard | `on-demand: ['mcp__*']` | bulk on-demand fallback | | default | no rule matched | — | resident | Key points: - **Exact rules win over wildcards (even across tiers)**: e.g. with `resident: ['mcp__gongfeng__*']` in place, clicking a tool to On-demand in the Capability Management writes an exact `on-demand` rule that takes effect instead of being pushed back by the wildcard (if a higher-priority rule still overrides it, the UI reports that the classification did not apply). > **Two kinds of change, both persisted**: > > - **Tier classification** changes memory first (so a click takes effect immediately) and is written back to this plugin's entry `config` (`patchFile`, the home layer's `~/.dsh/cordis.patch.yml` by default) once you stop for ~1.5s — writing that file makes dsh hot-reload this plugin and re-run the capability enumeration, so it cannot happen on every click. To batch-declare rules under version control, edit that same entry; no import/export buttons are needed. > - **Registered sources** (MCP servers, skill directories) hit disk as you click: MCP rows go into the same patch file (as `@deepseek-ai/dsh-mcp-client` entries), skill directories are linked into the skill root. ### On-demand capability catalog (`catalogFile`, the single materialized catalog, searchable with `grep`) On-demand capabilities are materialized into **one auto-generated YAML file** the model can browse: - The file location is the `config.catalogFile` of the **registry entry** (`capability-menu-registry`): it defaults to `~/.dsh/capability-catalog.yaml` and an empty string disables emission. The registry rewrites it automatically on any tool/skill or classification change. When nothing is On-demand, the catalog pointer is not injected (saving context). - A skill must first be **registered in `ctx.skills`** (a skill provider — e.g. its SKILL.md under a user/project skills root or `customSkillDirs`) to show up automatically; there is **no separate user-maintained input file**. - The model browses the file with `grep`/`read` (or calls `meta_search`) to get an entry's id and `kind`, then calls `meta_invoke(id, kind)` to run/load it. Skill ids are the bare name (e.g. `frontend-design`); `kind` distinguishes tools from skills. ```yaml # ~/.dsh/capability-catalog.yaml (auto-generated; contains only On-demand # capabilities — Resident ones are already resident and Disabled ones must not # be discoverable, so neither is written. Lists are emitted as `-` block # sequences, one item per line.) capabilities: - id: mcp__km__search kind: tool name: mcp__km__search description: Search the knowledge base server: km - id: legacy_skill kind: skill name: legacy_skill description: A low-frequency skill for working on legacy code whenToUse: Use when working on legacy projects ``` > The catalog file is written under the host's `~/.dsh` by default, so the sandbox of the model-side `bash`/`read` tools must be able to reach that path. If the sandbox isolates the host directory, explicitly configure `catalogFile` to a path the sandbox can see. The default path is shared across multiple dsh instances (last-write-wins); in multi-instance deployments, give each instance its own `catalogFile`. ## License This project is licensed under the [Apache License 2.0](LICENSE).