Pre-warm wrapper for the context-mode MCP server. Indexes configured markdown files into the FTS5 search database before the server starts, so search() returns results immediately — no manual indexing step required.
- Node.js (v22+) — runtime (context-mode depends on
better-sqlite3, a native addon not yet supported by bun's runtime) - A package manager: bun (preferred), pnpm, or npm
cd context-wrapper/
node setup.js # or: bun run setup.jsThis installs dependencies and prints the claude mcp add command to wire up the wrapper. Run that command to register it with Claude Code.
If you already have a context-mode entry in your .mcp.json, replace it — this wrapper supersedes the direct bundle invocation.
Create .claude/context-mode.json in any project that should have pre-warmed content:
Each source needs a label and a file selection strategy. Three strategies are available:
Match files by pattern in a directory.
{
"label": "work-logs",
"path": "/absolute/path/to/logs",
"glob": "*.md",
"stripFrontmatter": true,
"prefixDates": true
}Add "recursive": true to scan subdirectories:
{
"label": "docs",
"path": "/absolute/path/to/docs",
"glob": "*.md",
"recursive": true,
"stripFrontmatter": true
}Run a command that outputs a JSON array of file paths. Relative paths resolve from path (or CWD if omitted).
{
"label": "curated",
"path": "/project/root",
"exec": "./list-indexable-files.sh",
"stripFrontmatter": true
}The command must print valid JSON to stdout, e.g.:
["docs/guide.md", "docs/api/reference.md", "CHANGELOG.md"]Timeout: 10 seconds.
Hardcode a list of file paths. Relative paths resolve from path (or CWD if omitted).
{
"label": "key-docs",
"paths": [
"/absolute/path/to/architecture.md",
"/absolute/path/to/decisions.md"
],
"stripFrontmatter": true
}| Field | Description |
|---|---|
label |
(required) Source tag for scoped search (source: "work-logs") |
path |
Base directory. Required for glob, optional for exec/paths (used as CWD / base for relative paths) |
glob |
File pattern to match (e.g. *.md). Requires path. |
recursive |
Walk subdirectories when using glob. Default: false |
exec |
Shell command that outputs a JSON array of file paths |
paths |
Explicit array of file paths |
stripFrontmatter |
Remove YAML --- frontmatter blocks from start of files. Default: false |
prefixDates |
For date-named files (2026-02-28.md): prefix ## headings with [YYYY-MM-DD]. Default: false |
Pre-warmed content is searchable through the standard context-mode tools:
search(queries: ["tmux configuration"])
search(queries: ["authentication"], source: "work-logs")
search(queries: ["FTS5 schema"], source: "research-notes")
The source parameter matches against labels. A source labeled "work-logs" creates entries like "work-logs: 2026-02-28.md", so source: "work-logs" matches all files in that source.
In addition to pre-warm (which runs at startup), the wrapper exposes an index_folder tool for on-demand indexing of entire directories. Each file becomes a separate searchable source with dedup-by-label — re-indexing the same folder replaces previous content.
index_folder(path: "/path/to/docs")
index_folder(path: "/path/to/docs", glob: "*.txt", source: "my-docs")
| Parameter | Required | Default | Description |
|---|---|---|---|
path |
✅ | — | Directory to index |
glob |
*.md |
Filename pattern to match | |
recursive |
true |
Walk subdirectories | |
source |
dir basename | Label prefix — each file gets "{source}: {relative/path}" |
|
stripFrontmatter |
true |
Strip YAML frontmatter before indexing |
Files are preprocessed (frontmatter stripping, blank line collapsing), then forwarded individually to the upstream indexer. The response reports total files and chunks indexed.
The wrapper runs three phases on startup:
- Discover — Walks up from CWD looking for
.claude/context-mode.json - Pre-warm — Creates a SQLite FTS5 database at
/tmp/context-mode-{PID}.db, populates it with preprocessed and chunked content from the configured sources - Launch — Dynamic-imports the context-mode server bundle, which finds the pre-warmed database and serves it
If no config file is found, the wrapper skips pre-warming and starts the server normally — identical behavior to running context-mode directly.
Removes YAML frontmatter at the start of a file (between opening --- and closing ---). Does not affect horizontal rules mid-document.
For files named YYYY-MM-DD.md (work logs):
- Bare date headings (
## 2026-02-28) are removed - Topic headings become
## [2026-02-28] Database Migration - This makes search results self-documenting — you can see when something happened without checking the source filename
The upstream context-mode server progressively throttles search() calls and appends warning messages after the third call in a 60-second window. If these warnings create unwanted cognitive overhead, you can control them:
{
"sources": [ ... ],
"searchReminder": false
}| Value | Effect |
|---|---|
| (absent) | Warnings pass through unchanged (default) |
false |
Strip all throttle warnings and block messages |
"custom text" |
Replace warnings with your own text |
Note: This only controls the message — the upstream throttling behavior (reduced results per query after 3 calls, hard block after 8) still applies.
The wrapper includes subagent-hook.mjs — a PreToolUse hook that teaches subagents to use context-mode tools instead of flooding the parent's context with raw output.
What it does:
- Injects routing instructions into every subagent prompt, directing them to use
batch_execute,search,execute_file, etc. - Tells subagents to keep responses under 500 words and index detailed findings into the shared knowledge base (the parent can
search()for them afterward) - Upgrades Bash subagents to general-purpose so they gain MCP tool access
To enable it, add a hook entry to .claude/settings.json or .claude/settings.local.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Agent",
"hooks": [
{
"type": "command",
"command": "node /absolute/path/to/context-wrapper/subagent-hook.mjs"
}
]
}
]
}
}Replace the path with the absolute path to your context-wrapper/ directory.
Note: This hook nudges subagents toward context-mode tools but does not block standard tools. Subagents can still use Bash, Read, etc. when appropriate.
By default, all subagents get a 500-word response cap and dead-drop instructions, except Plan agents (full output) and a few skip types. You can override this per agent type in your .claude/context-mode.json:
{
"sources": [ ... ],
"subagentProfiles": {
"review": { "ending": "plan" },
"my-fetcher": { "skip": true },
"custom-role": { "block": "<custom>Full replacement injection text</custom>" }
}
}Each profile key matches against subagent_type. Three modes (mutually exclusive):
| Field | Effect |
|---|---|
skip: true |
No routing injected — agent runs unmodified |
ending: "plan" or "concise" |
Uses the standard tool routing block with the named output constraints |
block: "..." |
Full custom text, replaces the entire routing injection |
Custom profiles take priority over all hardcoded defaults. You can override the built-in Plan, Bash, or skip-type behavior by defining a profile with that name.
Copy this folder to another machine, run node setup.js, add the MCP server. The wrapper brings its own context-mode and better-sqlite3 as dependencies — only Node.js and a package manager (bun, pnpm, or npm) are required.