Context window optimization for AI coding agents. Sandboxes tool output (98% reduction), persists session memory, and enforces routing across 17 platforms via MCP + hooks.
Overview
Every MCP tool call dumps raw data into your context window. A Playwright snapshot costs 56 KB. Twenty GitHub issues cost 59 KB. One access log — 45 KB. After 30 minutes, 40% of your context is gone. And when the agent compacts the conversation to free space, it forgets which files it was editing, what tasks are in progress, and what you last asked for. On top of that, the agent wastes output tokens on filler, pleasantries, and verbose explanations — burning context from both sides. Context Mode is an MCP server that solves all four sides of this problem: 1. — Sandbox tools keep raw data out of the context window. 315 KB becomes 5.4 KB. 98% reduction. 2. — Every file edit, git operation, task, error, and user decision is tracked in SQLite. When the conversation compacts, context-mode doesn't dump this data back into context — it indexes events into FTS5 and retrieves only what's relevant via BM25 search. The model picks up exactly where you left off.
README
Context Mode
The other half of the context problem.
Used across teams at
The Problem
Every MCP tool call dumps raw data into your context window. A Playwright snapshot costs 56 KB. Twenty GitHub issues cost 59 KB. One access log — 45 KB. After 30 minutes, 40% of your context is gone. And when the agent compacts the conversation to free space, it forgets which files it was editing, what tasks are in progress, and what you last asked for. On top of that, the agent wastes output tokens on filler, pleasantries, and verbose explanations — burning context from both sides.
How Context Mode Solves It
Context Mode is an MCP server that solves all four sides of this problem:
-
Context Saving — Sandbox tools keep raw data out of the context window. 315 KB becomes 5.4 KB. 98% reduction.
-
Session Continuity — Every file edit, git operation, task, error, and user decision is tracked in SQLite. When the conversation compacts, context-mode doesn’t dump this data back into context — it indexes events into FTS5 and retrieves only what’s relevant via BM25 search. The model picks up exactly where you left off. If you don’t
--continue, previous session data is deleted immediately — a fresh session means a clean slate. -
Think in Code — The LLM should program the analysis, not compute it. Instead of reading 50 files into context to count functions, the agent writes a script that does the counting and
console.log()s only the result. One script replaces ten tool calls and saves 100x context. This is a mandatory paradigm across all 17 supported clients, plus the OpenClaw gateway integration: stop treating the LLM as a data processor, treat it as a code generator.// Before: 47 × Read() = 700 KB. After: 1 × ctx_execute() = 3.6 KB. ctx_execute("javascript", ` const files = fs.readdirSync('src').filter(f => f.endsWith('.ts')); files.forEach(f => console.log(f + ': ' + fs.readFileSync('src/'+f,'utf8').split('\\n').length + ' lines')); `); -
No prose-style enforcement — context-mode keeps raw data out of context but never dictates how the model writes its final answer. Brevity, completeness, formatting — your model’s call (or yours via your own
CLAUDE.md/AGENTS.md). Aggressive brevity prompts have been shown to degrade coding/reasoning benchmarks (Moonshot AI onkimi-k2.5) — the routing block stays focused on where data goes, not on how the model talks.
Install
Platforms are grouped by install complexity. Hook-capable platforms get automatic routing enforcement. Non-hook platforms need a one-time routing file copy.
Tools
| Tool | What it does | Context saved |
|---|---|---|
ctx_batch_execute |
Run multiple commands + search multiple queries in ONE call. Opt-in concurrency: 1-8 for I/O-bound batches. |
986 KB → 62 KB |
ctx_execute |
Run code in 12 languages. Only stdout enters context. | 56 KB → 299 B |
ctx_execute_file |
Process files in sandbox. Raw content never leaves. | 45 KB → 155 B |
ctx_index |
Chunk markdown into FTS5 with BM25 ranking. | 60 KB → 40 B |
ctx_search |
Query indexed content with multiple queries in one call. | On-demand retrieval |
ctx_fetch_and_index |
Fetch URL, chunk and index. Cache reuses content within TTL (default 24h, override per-call with ttl: ). ttl: 0 or force: true to bypass. Pass requests: [{url, source}, ...] + concurrency: 1-8 for parallel multi-URL. |
60 KB → 40 B |
ctx_stats |
Show context savings, call counts, and session statistics. | — |
ctx_doctor |
Diagnose installation: runtimes, hooks, FTS5, versions. | — |
ctx_upgrade |
Upgrade to latest version from GitHub, rebuild, reconfigure hooks. | — |
ctx_purge |
Permanently deletes all indexed content from the knowledge base. | — |
How the Sandbox Works
Each ctx_execute call spawns an isolated subprocess with its own process boundary. Scripts can’t access each other’s memory or state. The subprocess runs your code, captures stdout, and only that stdout enters the conversation context. The raw data — log files, API responses, snapshots — never leaves the sandbox.
Twelve language runtimes are available: JavaScript, TypeScript, Python, Shell, Ruby, Go, Rust, PHP, Perl, R, Elixir, and C#. Bun is auto-detected for 3-5x faster JS/TS execution.
Authenticated CLIs work through credential passthrough — gh, aws, gcloud, kubectl, docker inherit environment variables and config paths without exposing them to the conversation.
When output exceeds 5 KB and an intent is provided, Context Mode switches to intent-driven filtering: it indexes the full output into the knowledge base, searches for sections matching your intent, and returns only the relevant matches with a vocabulary of searchable terms for follow-up queries.
How the Knowledge Base Works
The ctx_index tool chunks markdown content by headings while keeping code blocks intact, then stores them in a SQLite FTS5 (Full-Text Search 5) virtual table. The SQLite backend is selected automatically at runtime: bun:sqlite on Bun, node:sqlite on Node.js >= 22.5, and better-sqlite3 everywhere else. Search uses BM25 ranking — a probabilistic relevance algorithm that scores documents based on term frequency, inverse document frequency, and document length normalization. Porter stemming is applied at index time so “running”, “runs”, and “ran” match the same stem. Titles and headings are weighted 5x in BM25 scoring for precise navigational queries.
When you call ctx_search, it returns relevant content snippets focused around matching query terms — not full documents, not approximations, the actual indexed content with smart extraction around what you’re looking for. ctx_fetch_and_index extends this to URLs: fetch, convert HTML to markdown, chunk, index. The raw page never enters context. Use the contentType parameter to filter results by type (e.g. code or prose).
Ranking: Reciprocal Rank Fusion
Search runs two parallel strategies and merges them with Reciprocal Rank Fusion (RRF):
- Porter stemming — FTS5 MATCH with porter tokenizer. “caching” matches “cached”, “caches”, “cach”.
- Trigram substring — FTS5 trigram tokenizer matches partial strings. “useEff” finds “useEffect”, “authenticat” finds “authentication”.
RRF merges both ranked lists into a single result set, so a document that ranks well in both strategies surfaces higher than one that ranks well in only one. This replaces the old cascading fallback approach where trigram results were only used if porter returned nothing.
Proximity Reranking
Multi-term queries get an additional reranking pass. Results where query terms appear close together are boosted — "session continuity" ranks passages with adjacent terms higher than pages where “session” and “continuity” appear paragraphs apart.
Fuzzy Correction
Levenshtein distance corrects typos before re-searching. “kuberntes” becomes “kubernetes”, “autentication” becomes “authentication”.
Smart Snippets
Search results use intelligent extraction instead of truncation. Instead of returning the first N characters (which might miss the important part), Context Mode finds where your query terms appear in the content and returns windows around those matches.
TTL Cache
Indexed content persists in a per-project SQLite database at ~/.context-mode/content/. When ctx_fetch_and_index is called for a URL that was already indexed within its TTL window, the fetch is skipped entirely and the model searches the existing index directly.
- Default TTL: 24 hours. Override per-call with
ttl:(PR #666). Longer for stable specs, shorter for changelogs you want re-checked often. - Cache hit (within TTL): Returns a cache hint (~0.3KB) instead of re-fetching (48KB+). Model proceeds to
ctx_search. - Cache miss (TTL expired): Re-fetches silently. No user action needed.
ttl: 0orforce: true: Bypasses cache and re-fetches regardless of freshness.- 14-day cleanup: Content databases and sources older than 14 days are removed on startup.
This means --continue sessions preserve indexed docs across restarts. No re-fetching, no wasted context tokens.
ctx_stats reports cache performance separately: hits, data avoided, network requests saved, and total context savings including cache.
Progressive Throttling
- Calls 1-3: Normal results (2 per query)
- Calls 4-8: Reduced results (1 per query) + warning
- Calls 9+: Blocked — redirects to
ctx_batch_execute
Session Continuity
When the context window fills up, the agent compacts the conversation — dropping older messages to make room. Without session tracking, the model forgets which files it was editing, what tasks are in progress, what errors were resolved, and what you last asked for.
Context Mode captures every meaningful event during your session and persists them in a per-project SQLite database. When the conversation compacts (or you resume with --continue, --resume, or /resume), your working state is rebuilt automatically — the model continues from your last prompt without asking you to repeat anything.
Resuming a non-latest session via
/resumeworks the same way: the SessionStart hook detects the empty live-event table for the freshly issued session id and falls back to the most recent unconsumed snapshot for the project (session_resumetable). The picker selects the conversation; context-mode rehydrates the prior working state.
Session continuity requires 5 hooks working together:
| Hook | Role | Claude Code | Gemini CLI | VS Code Copilot | JetBrains Copilot | GitHub Copilot CLI | Cursor | OpenCode | KiloCode | OpenClaw | Codex CLI | Antigravity | Antigravity CLI (agy) |
Kiro | Zed | Pi | OMP |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| PreToolUse | Enforces sandbox routing before tool execution | Yes | – | – | – | Yes | Yes | – | – | – | Yes | – | Bounded | Yes | – | ✓ (via tool_call event) | ✓ (via tool_call event) |
| PostToolUse | Captures events after each tool call | Yes | Yes | Yes | Yes | Yes | Yes | Plugin | Plugin | Plugin | Yes | – | Yes (capture-only) | Yes | – | ✓ (via tool_result event) | ✓ (via tool_result event) |
| UserPromptSubmit | Captures user decisions and corrections | Yes | – | – | – | Yes | – | Plugin (via chat.message) | Plugin (via chat.message) | – | Yes | – | – | – | – | – | – |
| Stop | Captures assistant turn-end state | Yes | – | – | – | Yes | Yes | – | – | – | Yes | – | Best-effort | – | – | – | – |
| PreCompact | Builds snapshot before compaction | Yes | Yes | Yes | Yes | Yes | – | Plugin | Plugin | Plugin | Yes | – | – | – | – | ✓ (via session_before_compact) | ✓ (via session_before_compact) |
| SessionStart | Restores state after compaction or resume | Yes | Yes | Yes | Yes | Yes | – | ✓ (via experimental.chat.system.transform) | ✓ (via experimental.chat.system.transform) | Plugin | Yes | – | – | – | – | ✓ (via session_start event) | ✓ (via session_start event) |
| Session completeness | Full | High | High | High | High | Partial | Full | Full | High | Partial | – | Partial | Partial | – | High | High |
Note: Full session continuity (capture + snapshot + restore) works on Claude Code, Gemini CLI, VS Code Copilot, JetBrains Copilot, OpenCode, and KiloCode. GitHub Copilot CLI uses its own camelCase hook config keys (
preToolUse,postToolUse,preCompact,sessionStart,userPromptSubmitted,agentStop) and top-level hook responses; it captures prompt, tool, compaction, session-start, and stop events when the plugin hooks are installed. OpenCode and KiloCode useexperimental.chat.system.transformas a SessionStart surrogate to inject the routing block and restore prior sessions, pluschat.messagefor user-prompt capture; full SessionStart hook support is not yet available (#14808, #5409), but prior-session continuity and user-decision capture work fully. Cursor captures tool events viapreToolUse/postToolUse, butsessionStartis currently rejected by Cursor’s validator (forum report), so session restore after compaction is not available yet. OpenClaw uses native gateway plugin hooks (api.on()) for full session continuity. Pi Coding Agent provides high session continuity via extension hooks (tool_call,tool_result,session_start,session_before_compact). Codex CLI provides partial hook-based session tracking through PreToolUse, PostToolUse, PreCompact, SessionStart, UserPromptSubmit, and Stop; MCP tools work. Antigravity IDE and Zed have no hook support in the current release, so session tracking is not available there. Antigravity CLI (agy) is separate from the IDE and supports boundedPreToolUse, capture-onlyPostToolUse, and best-effortStopthrough its plugin hooks. Kiro captures tool events via nativepreToolUse/postToolUsehooks, but its SessionStart equivalent (agentSpawn) is not yet wired, so session restore after compaction is unavailable. OMP (Oh My Pi) ships full plugin-based hook support —omp plugin install context-moderegisterstool_call,tool_result,session_start, andsession_before_compacthandlers and storage roots cleanly under~/.omp/context-mode/so OMP and Pi installs never share state.
Platform Compatibility
| Feature | Claude Code | Qwen Code | Gemini CLI | VS Code Copilot | JetBrains Copilot | GitHub Copilot CLI | Cursor | OpenCode | KiloCode | OpenClaw | Codex CLI | Kimi Code | Antigravity | Antigravity CLI (agy) |
Kiro | Zed | Pi | OMP |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| MCP Server / Native Tools | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Native plugin | Native plugin | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| PreToolUse Hook | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Plugin | Plugin | Plugin | Yes | Yes | – | Bounded | Yes | – | Yes (extension) | Plugin |
| PostToolUse Hook | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Plugin | Plugin | Plugin | Yes | Yes | – | Yes (capture-only) | Yes | – | Yes (extension) | Plugin |
| SessionStart Hook | Yes | Yes | Yes | Yes | Yes | Yes | – | ✓ (via experimental.chat.system.transform) | ✓ (via experimental.chat.system.transform) | Plugin | Yes | Yes | – | – | – | – | Yes (extension) | Plugin |
| PreCompact Hook | Yes | Yes | Yes | Yes | Yes | Yes | – | Plugin | Plugin | Plugin | Yes | Yes | – | – | – | – | Yes (extension) | Plugin |
| Can Modify Args | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Plugin | Plugin | Plugin | – | Yes | – | – | – | – | Yes (extension) | – |
| Can Block Tools | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Plugin | Plugin | Plugin | Yes | Yes | – | Bounded | Yes | – | Yes (extension) | Plugin |
| Utility Commands (ctx) | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes (/ctx-stats, /ctx-doctor) | Yes |
| Slash Commands | Yes | – | – | – | – | – | – | – | – | – | – | – | – | – | – | – | – | – |
| Plugin Marketplace | Yes | – | – | – | – | – | – | – | – | – | – | – | – | – | – | – | – | – |
OpenCode uses a TypeScript plugin paradigm — hooks run as in-process functions via
tool.execute.before,tool.execute.after,experimental.session.compacting,experimental.chat.system.transform, andchat.message, providing full routing enforcement, session continuity, and user-prompt capture. Theexperimental.chat.system.transformhook acts as a SessionStart surrogate to inject the routing block and restore prior sessions. Thechat.messagehook captures user prompts and decisions (UserPromptSubmit equivalent).KiloCode shares the same TypeScript plugin architecture as OpenCode via the OpenCodeAdapter, with platform-specific configuration paths (
kilo.jsoninstead ofopencode.json,~/.config/kilo/instead of~/.config/opencode/). Hook capabilities match OpenCode, including SessionStart surrogate viaexperimental.chat.system.transformand user-prompt capture viachat.message.OpenClaw runs context-mode as a native gateway plugin targeting Pi Agent sessions. Hooks register via
api.on()(tool/lifecycle) andapi.registerHook()(commands). All tool interception and compaction hooks are supported. Seedocs/adapters/openclaw.md.Codex CLI hooks require
[features].hooks = true. MCP tools work, and hook scripts activate through$CODEX_HOME/hooks.jsonor~/.codex/hooks.json. PreToolUse supportspermissionDecision: "deny"only; input modification still needs upstreamupdatedInputsupport (openai/codex#18491).additionalContextis not supported in PreToolUse (context injection works via PostToolUse and SessionStart instead; the codex formatter handles this automatically). PreCompact stores resume snapshots before compaction on Codex builds that emit the event, SessionStart restores them, and UserPromptSubmit/Stop capture prompt and turn-end continuity events. See the Codex install section for setup. Antigravity and Zed do not support hooks. They rely solely on manually-copied routing instruction files (AGENTS.md/GEMINI.md) for enforcement (~60% compliance). See each platform’s install section for copy instructions. Antigravity and Zed are auto-detected via MCP protocol handshake — no manual platform configuration needed.Antigravity CLI (
agy) supports boundedPreToolUseblocking for mapped Bash/Read/Grep/WebFetch surfaces, plusPostToolUsecapture and best-effortStopcapture through its pluginhooks.json. The routing rule and routing skill remain the broader instruction layer;PreInvocation/PostInvocationare not wired until their payload/response semantics are verified.Kiro supports native
preToolUseandpostToolUsehooks for routing enforcement and tool event capture.agentSpawn(SessionStart equivalent) andstopare not yet wired. Requires manually copyingKIRO.mdto your project root. Kiro is auto-detected via MCP protocol handshake (clientInfo.name).Pi Coding Agent runs context-mode as an extension with full hook support. The extension registers
tool_call,tool_result,session_start, andsession_before_compactevents, providing high session continuity coverage. The MCP server provides all 11 MCP tools.OMP (Oh My Pi) runs context-mode as a plugin via
omp plugin install context-mode. The plugin registerstool_call,tool_result,session_start, andsession_before_compactevents for hard-block routing and full session continuity. Storage isolated under~/.omp/context-mode/so OMP and Pi never share state. Auto-detected viaPI_CODING_AGENT_DIR(default agent dir~/.omp/agent) or~/.omp/directory. See issue #473 for the storage-isolation history.
Routing Enforcement
Hooks intercept tool calls programmatically — they can block dangerous commands and redirect them to the sandbox before execution. Instruction files guide the model via prompt instructions but cannot block anything. Always enable hooks where supported.
Note: Routing instruction files were previously auto-written to project directories on first session start. This was disabled to prevent git tree pollution (#158, #164). Hook-capable platforms (Claude Code, Gemini CLI, VS Code Copilot, JetBrains Copilot, GitHub Copilot CLI, Cursor, OpenCode, OpenClaw, Codex CLI, Antigravity CLI for bounded tool hooks, Kiro for tool hooks, OMP via plugin) inject or enforce routing without writing files. Platforms without hook support — Zed and Antigravity IDE — require a one-time manual copy of the routing file; see each platform’s install section.
| Platform | Hooks | Instruction File | With Hooks | Without Hooks |
|---|---|---|---|---|
| Claude Code | Yes (auto) | CLAUDE.md |
~98% saved | ~60% saved |
| Gemini CLI | Yes | GEMINI.md |
~98% saved | ~60% saved |
| VS Code Copilot | Yes | copilot-instructions.md |
~98% saved | ~60% saved |
| JetBrains Copilot | Yes | copilot-instructions.md |
~98% saved | ~60% saved |
| GitHub Copilot CLI | Yes | copilot-instructions.md |
~98% saved | ~60% saved |
| Cursor | Yes | context-mode.mdc |
~98% saved | ~60% saved |
| OpenCode | Plugin | AGENTS.md |
~98% saved | ~60% saved |
| OpenClaw | Plugin | AGENTS.md |
~98% saved | ~60% saved |
| Codex CLI | Yes | AGENTS.md |
~98% saved | ~60% saved |
| Antigravity | – | GEMINI.md |
– | ~60% saved |
Antigravity CLI (agy) |
Bounded | routing rule + skill (rules, skill) |
bounded Bash/Read/Grep/WebFetch enforcement | ~60% saved |
| Kiro | Yes | KIRO.md |
~98% saved | ~60% saved |
| Zed | – | AGENTS.md |
– | ~60% saved |
| Pi | ✓ | AGENTS.md |
~98% saved | ~60% saved |
| OMP | Plugin | SYSTEM.md |
~98% saved | ~60% saved |
Without hooks, one unrouted curl or Playwright snapshot can dump 56 KB into context — wiping out an entire session’s worth of savings.
See docs/platform-support.md for the full capability comparison.
Utility Commands
Inside any AI session — just type the command. The LLM calls the MCP tool automatically:
ctx stats → context savings, call counts, session report
ctx doctor → diagnose runtimes, hooks, FTS5, versions
ctx index → index a local file or directory for later search
ctx search → search previously indexed content
ctx upgrade → update from GitHub, rebuild, reconfigure hooks
ctx purge → permanently delete all indexed content from the knowledge base
ctx insight → opens the hosted Insight dashboard in your browser
From your terminal — run directly without an AI session:
context-mode doctor
context-mode index . --source project:my-app
context-mode search "authentication middleware" --source project:my-app
context-mode upgrade
context-mode insight # opens the hosted Insight dashboard in browser
bash scripts/ctx-debug.sh # full diagnostic report for bug reports
The debug script collects OS info, runtime versions, better-sqlite3 status, adapter detection, config files (redacted), hook validation, FTS5/SQLite test, executor test, process check, session databases, and environment variables into a single pasteable markdown report.
Works on all platforms. On Claude Code, slash commands (/ctx-stats, /ctx-doctor, /ctx-index, /ctx-search, /ctx-upgrade, /ctx-purge, /ctx-insight) are also available.
Benchmarks
| Scenario | Raw | Context | Saved |
|---|---|---|---|
| Playwright snapshot | 56.2 KB | 299 B | 99% |
| GitHub Issues (20) | 58.9 KB | 1.1 KB | 98% |
| Access log (500 requests) | 45.1 KB | 155 B | 100% |
| Context7 React docs | 5.9 KB | 261 B | 96% |
| Analytics CSV (500 rows) | 85.5 KB | 222 B | 100% |
| Git log (153 commits) | 11.6 KB | 107 B | 99% |
| Test output (30 suites) | 6.0 KB | 337 B | 95% |
| Repo research (subagent) | 986 KB | 62 KB | 94% |
Over a full session: 315 KB of raw output becomes 5.4 KB. Session time extends from ~30 minutes to ~3 hours.
Full benchmark data with 21 scenarios →
Try It
These prompts work out of the box. Run /context-mode:ctx-stats after each to see the savings.
Deep repo research — 5 calls, 62 KB context (raw: 986 KB, 94% saved)
Research https://github.com/modelcontextprotocol/servers — architecture, tech stack,
top contributors, open issues, and recent activity. Then run /context-mode:ctx-stats.
Git history analysis — 1 call, 5.6 KB context
Clone https://github.com/facebook/react and analyze the last 500 commits:
top contributors, commit frequency by month, and most changed files.
Then run /context-mode:ctx-stats.
Web scraping — 1 call, 3.2 KB context
Fetch the Hacker News front page, extract all posts with titles, scores,
and domains. Group by domain. Then run /context-mode:ctx-stats.
Large JSON API — 7.5 MB raw → 0.9 KB context (99% saved)
Create a local server that returns a 7.5 MB JSON with 20,000 records and a secret
hidden at index 13000. Fetch the endpoint, find the hidden record, and show me
exactly what's in it. Then run /context-mode:ctx-stats.
Documentation search — 2 calls, 1.8 KB context
Fetch the React useEffect docs, index them, and find the cleanup pattern
with code examples. Then run /context-mode:ctx-stats.
Session continuity — compaction recovery with full state
Start a multi-step task: "Create a REST API with Express — add routes, tests,
and error handling." After 20+ tool calls, type: ctx stats to see the session
event count. When context compacts, the model continues from your last prompt
with tasks, files, and decisions intact — no re-prompting needed.
Privacy & Architecture
Context Mode is not a CLI output filter or a cloud analytics dashboard. It operates at the MCP protocol layer — raw data stays in a sandboxed subprocess and never enters your context window. Web pages, API responses, file analysis, Playwright snapshots, log files — everything is processed in complete isolation.
Nothing leaves your machine. No telemetry, no cloud sync, no usage tracking, no account required. Your code, your prompts, your session data — all local. The SQLite databases live in your home directory and die when you’re done.
This is a deliberate architectural choice, not a missing feature. Context optimization should happen at the source, not in a dashboard behind a per-seat subscription. Privacy-first is our philosophy — and every design decision follows from it. License →
Security
Context Mode enforces the same permission rules you already use — but extends them to the MCP sandbox. If you block sudo, it’s also blocked inside ctx_execute, ctx_execute_file, and ctx_batch_execute.
Zero setup required. If you haven’t configured any permissions, nothing changes. This only activates when you add rules.
{
"permissions": {
"deny": [
"Bash(sudo *)",
"Bash(rm -rf /*)",
"Read(.env)",
"Read(**/.env*)"
],
"allow": [
"Bash(git:*)",
"Bash(npm:*)"
]
}
}
Add this to your project’s .claude/settings.json (or ~/.claude/settings.json for global rules). All platforms read security policies from Claude Code’s settings format — even on Gemini CLI, VS Code Copilot, and OpenCode. Codex CLI security enforcement requires the Codex hooks in $CODEX_HOME/hooks.json or ~/.codex/hooks.json to be configured.
The pattern is Tool(what to match) where * means “anything”.
Commands chained with &&, ;, or | are split — each part is checked separately. echo hello && sudo rm -rf /tmp is blocked because the sudo part matches the deny rule.
deny always wins over allow. More specific (project-level) rules override global ones.
Project-boundary containment
ctx_execute_file is confined to the project root. A path that resolves outside the workspace — an absolute path like /home/user/secrets, a ../../ traversal, or a project-local symlink whose target escapes the project — is refused with a File access blocked error. This closes the #852 escape vector where an agent, denied an out-of-project read by the host sandbox, retried through the MCP sandbox (the host’s MCP approval prompt cannot inspect the tool’s input params, so the escape was invisible to the approver).
The guard is on by default and requires no configuration. To intentionally process a file outside the project (e.g. a shared log under /var/log), opt that path back in with the same permissions.allow rule you already use for the host Read tool — there is no context-mode-specific env flag:
{
"permissions": {
"allow": ["Read(/var/log/**)"]
}
}
context-mode honors that allow rule (read from your .claude/settings.json / ~/.claude/settings.json) exactly as Claude Code does, so an out-of-project grant lives in one place and stays meaningful.
Reviewing the prompt: the ctx_execute / ctx_execute_file approval titles now read as code execution (“Run code in a sandbox…”, “Run code over a file…”) so an unfamiliar reviewer can recognise the action class even though the MCP prompt renders only the tool title and raw arguments. ctx_execute and ctx_batch_execute run arbitrary code and still inherit the process’s filesystem access, so the boundary guard is a defense-in-depth layer for the file-read tool, not a full OS sandbox — treat approving any execution tool as approving arbitrary code, and keep host-level sandboxing enabled.
Network fetch hardening
ctx_fetch_and_index blocks dangerous URL targets by default:
- Schemes: only
http:andhttps:allowed (nofile://,gopher://,javascript:,data:). - Cloud metadata + link-local:
169.254.0.0/16(incl. AWS/GCP/Azure IMDS endpoint169.254.169.254) hard-blocked even if a hostname resolves to it (DNS-rebinding defense). - Multicast / reserved:
224.0.0.0/4,0.0.0.0/8, IPv6ff00::/8,fe80::/10blocked. - Loopback + RFC1918 (
localhost,127.x,10.x,172.16-31.x,192.168.x, IPv6::1,fc00::/7) allowed by default so local dev servers + internal-network fetches keep working.
For hosted/CI environments where you want to block private targets too, set:
export CTX_FETCH_STRICT=1
That blocks loopback + RFC1918 + ULA in addition to the always-blocked ranges. Useful when context-mode runs as a shared service, not on a developer’s own machine.
tool_input for any mcp__* tool call is also redacted before persistence — the regex matcher in hooks/posttooluse.mjs masks authorization, auth_token, access_token, refresh_token, bearer, token, secret, password, passwd, pwd, api_key / apikey / x_api_key, cookie / set-cookie, signature, private_key, and client_secret (case-insensitive, hyphen/underscore-insensitive) to [REDACTED] so credentials in MCP arguments don’t end up in the session DB.
Storage environment variables
| Variable | Default | Purpose |
|---|---|---|
CONTEXT_MODE_DIR |
Adapter default, for example ~/.codex/context-mode or ~/.claude/context-mode |
Since v1.0.147. Absolute writable root for context-mode storage. Sessions and stats use /sessions; indexed content uses /content. Empty or whitespace-only values are treated as unset and shown by ctx_doctor; non-empty values must be absolute. ~ is not expanded. |
Routing-guidance environment variables
| Variable | Default | Purpose |
|---|---|---|
CONTEXT_MODE_EXTERNAL_MCP_NUDGE_EVERY |
10 |
Cadence (in tool calls) at which the PreToolUse hook re-injects the “wrap large external-MCP payloads in ctx_execute” guidance. The original implementation (#529) fired only once per session, which got lost after context compaction in MCP-heavy sessions (e.g. 50+ Jira/Slack/Notion calls — see #567 follow-up). The default re-fires every 10th matching call, keeping the guidance in the model’s recent window. Range [1, 100]; invalid values fall back to 10. Set to 1 for “every call” (most aggressive — adds ~250 tokens/call) or to a larger value for less frequent reminders. |
Contributing
See CONTRIBUTING.md for the development workflow and TDD guidelines.
git clone https://github.com/mksglu/context-mode.git
cd context-mode && npm install && npm test
License
Licensed under Elastic License 2.0 (source-available). You can use it, fork it, modify it, and distribute it. Two things you can’t do: offer it as a hosted/managed service, or remove the licensing notices. We chose ELv2 over MIT because MIT permits repackaging the code as a competing closed-source SaaS — ELv2 prevents that while keeping the source available to everyone.
Install
This server does not publish a one-line install command.
Open the repository installation guideConfiguration
{
"mcpServers": {
"context-mode": {
"command": "context-mode"
}
},
"hooks": {
"BeforeTool": [
{
"matcher": "run_shell_command|read_file|read_many_files|grep_search|search_file_content|web_fetch|activate_skill|mcp__plugin_context-mode|mcp__context-mode|mcp__(?!.*context-mode)",
"hooks": [{ "type": "command", "command": "context-mode hook gemini-cli beforetool" }]
}
],
"AfterTool": [
{
"matcher": "",
"hooks": [{ "type": "command", "command": "context-mode hook gemini-cli aftertool" }]
}
],
"PreCompress": [
{
"matcher": "",
"hooks": [{ "type": "command", "command": "context-mode hook gemini-cli precompress" }]
}
],
"SessionStart": [
{
"matcher": "",
"hooks": [{ "type": "command", "command": "context-mode hook gemini-cli sessionstart" }]
}
]
}
}