dsh-mcp-context-mode
DeepSeek Harness bundle: native ctx_* sandbox tools (no MCP child), routing gate, repaired HTTPS fetching, and session context-occupancy reporting.
- Stars
- 0
- Language
- JavaScript
- Created
- Oct 15, 2026
- Updated
- Oct 16, 2026
Introduction
dsh-mcp-context-mode
简体中文 | English
Give DeepSeek Harness a sandbox it can run code in, a searchable knowledge base it can file things into, and a routing gate that keeps bulk output out of the conversation.
Everything registers as plain ctx_* tools in the host process — no child process, no JSON-RPC hop, no mcp__ prefix in tool names.
What it is for
A conversation pays for every byte a tool returns, and it keeps paying for the rest of the session. This plugin moves the heavy work off that budget.
A settings page (Settings → Context mode, also a card in the Plugins panel) lists every switch this plugin has and shows a live read of the current session: total occupancy, the system / tool-schema / conversation split, the biggest contributors, and this plugin's own savings ledger. A switch that only takes effect at load time is marked as needing a restart instead of pretending it did something.
- Analyse without reading. Run code in a sandbox and print only the answer — the raw bytes never enter the conversation.
- File instead of paste. Put large material into a local full-text index, then pull back only the matching passages.
- Fetch without flooding. Retrieve a page into that index rather than into the transcript.
- See where the budget went. Ask for a breakdown of the current session's context occupancy.
Tools
Ten tools register by default, plus a context report. ctx_upgrade exists but ships switched off.
| Tool | What it does |
|---|---|
ctx_execute | Run code in a sandbox (JavaScript, TypeScript, Python, shell). Only what you print comes back. |
ctx_execute_file | Run code over a file, without the file's bytes entering the conversation. |
ctx_batch_execute | Run several commands at once; each output is indexed automatically, and only the sections matching your queries are returned. |
ctx_index | Store content in a local full-text (FTS5) knowledge base. |
ctx_search | Search that knowledge base. Batch several questions into one call. |
ctx_fetch_and_index | Fetch a URL and index it, returning only a small preview. |
ctx_stats | Session savings: bytes kept out of context and what they would have cost. |
ctx_doctor | Self-check: runtimes, storage, SQLite/FTS5, version. |
ctx_purge | Permanently delete indexed content (per session or whole project). |
ctx_insight | Open the companion analytics dashboard in a browser. |
ctx_context_report | Context occupancy for this session: totals, breakdown by category, compaction suggestions, and this plugin's own ledger. Read-only — it replays the session log and makes no model calls. |
Beyond the tools, a routing gate watches tool calls before they run: curl and wget are blocked outright (they paste whole pages into the conversation by design), long commands and oversized file reads get a nudge toward the tools above, and a small allowlist passes through untouched.
Install
Install straight from the repository:
dsh plugin --profile desktop add github:chimneyQ/dsh-mcp-context-mode
If pnpm stops with an ERR_PNPM_IGNORED_BUILDS message naming two dependencies, answer it with false — neither of them needs to compile:
# <DSH_HOME>/profiles/desktop/pnpm-workspace.yaml
allowBuilds:
context-mode: false
better-sqlite3: false
Then run the same command again.
Answer false, not true. This plugin's engine uses Node's built-in
node:sqlite(present since Node 22.5) and only falls back to the nativebetter-sqlite3where that is unavailable. Allowing the build makes pnpm compilebetter-sqlite3from source, which needs a C++ toolchain — on a machine without one it fails and the whole install aborts, leaving no plugin. Nothing needs building in the normal case.
From a local checkout, the same command takes the directory instead:
dsh plugin --profile desktop add D:\path\to\dsh-mcp-context-mode
Then restart DeepSeek Harness completely. The ctx_* tools appear in new sessions.
If the first attempt already failed
The host records the dependency before the build block is answered, and a second add on a profile that already lists it takes a different path: pnpm reports "Already up to date", the manifest keeps its entry, and the plugin is never added to dsh.profile.bundles — so it installs but never loads. Removing the entry first puts the profile back on the normal path:
# 1. answer the build block as shown above (keep allowBuilds false)
# 2. drop the half-written dependency
dsh plugin --profile desktop remove dsh-mcp-context-mode
# 3. install again — this time the bundle list is written
dsh plugin --profile desktop add github:chimneyQ/dsh-mcp-context-mode
Check it took: dsh.profile.bundles in <DSH_HOME>/profiles/desktop/package.json should list dsh-mcp-context-mode.
Use it well
| Situation | Better than reading it yourself |
|---|---|
| A large log, CSV, or JSON to analyse | ctx_execute_file — parse it in the sandbox, print the summary |
| Several commands whose output you need to compare | ctx_batch_execute with queries — gather and filter in one round trip |
| A long document or page you will quote from | ctx_fetch_and_index, then ctx_search for the passages |
| Material you may need again later | ctx_index once, ctx_search on demand |
| Wondering what is filling the context | ctx_context_report |
Reading a file in order to edit it is the one case where the host's own file tools are still right — they need the exact bytes.
Configuration
Every adjustable value lives in the plugin's Config, so setting it in cordis.patch.yml persists:
| Key | Default | Meaning |
|---|---|---|
storageDir | '' | Knowledge-base root; empty = <DSH_HOME>/context-mode |
enginePath | '' | Explicit engine entry; empty = resolve automatically |
caPemPath | 'auto' | TLS trust source: 'auto' writes a PEM into storageDir/ca; an absolute path is used as-is; '' disables |
routing | true | Master switch for the routing gate |
denyCurlWget | true | Block curl/wget outright |
bashNudgeMinCommandBytes | 4096 | Long-command nudge threshold (bytes) |
maxReadBytesBeforeAsk | 51200 | Oversized whole-file read nudge threshold (bytes) |
contextReport | true | Register ctx_context_report |
compressNudgeTokens | 120000 | Occupancy warning threshold (tokens) |
trustedReadBasenames | 4 names | Filenames exempt from the whole-read nudge |
tools.* | all true except upgrade | Per-tool switches |
Verify
The repository ships SHA256SUMS.txt describing exactly the bytes it publishes. From the repository root:
node -e "const fs=require('fs'),c=require('crypto');for(const l of fs.readFileSync('SHA256SUMS.txt','utf8').split(/\r?\n/)){if(!l.trim()||l.startsWith('#'))continue;const [h,f]=l.split(/\s+/);console.log((c.createHash('sha256').update(fs.readFileSync(f)).digest('hex')===h?'OK ':'BAD ')+f)}"
Offline self-checks. The first needs the dependency installed; the rest are self-contained:
npm ci
node smoke.mjs # 36 assertions: tools, gate, TLS, context report
node test-client.mjs # 17: the browser half's loader shape and field contract
node test-settings.mjs # 8: every page control is a volatile Config field
node test-route.mjs # 17: the settings page's data source, including every degraded path
Confirm the profile actually wired the plugin up (an install can leave the dependency recorded while never adding the plugin to dsh.profile.bundles, in which case it never loads):
node install-check.mjs --profile desktop
Known limits
- The routing gate is advisory: where an approval channel exists it asks, and where none exists the call proceeds. It is not a security boundary.
tls.setDefaultCACertificatesis a process-wide side effect; setcaPemPath: ''to turn it off.- Session restore/distillation is not wired up. For durable notes, use
ctx_indexexplicitly.
License
The plugin code in this repository is MIT (see LICENSE).
It depends on a third-party runtime engine, which is not distributed here: your package manager installs it, unmodified, under its own Elastic-2.0 license. The context-occupancy report borrows API usage from dsh-context (Apache-2.0, by bowenliang123); none of its code is copied.