About Ctx Saver
Ctx Saver is an MCP server published by ChonlakanSutthimatmongkhol in the Data category: a self-hosted MCP server (Go) that reduces AI context window usage by sandboxing large tool outputs and returning compact summaries instead of raw text.
The repository has 3 stars and 0 forks, with the last commit a month ago. Recent commit activity is the strongest signal that reported issues will get attention.
Install
git clone https://github.com/ChonlakanSutthimatmongkhol/ctx-saverThis server has no ConduID identity, so agent calls to it are not receipted. Pin the version you install and review the source before granting it credentials.
Tools (6)
ctx_executeRun shell, Python, Go, or Node commands while storing large output safely.
Parameters not published.
ctx_get_fullRetrieve a full output or exact line range when needed.
Parameters not published.
ctx_noteSave/list durable decisions, or use `action="handoff"` with `task="..."` to resume work across sessions.
Parameters not published.
ctx_read_fileRead large files without flooding the model context.
Parameters not published.
ctx_searchSearch stored outputs using full-text search.
Parameters not published.
ctx_session_initStart a session with project rules, recent activity, cached outputs, cached reference files (`cached_files`), and saved decisions. Pass `task="..."` to resume task-scoped handoffs.
Parameters not published.
Ask AI
Ask AI about Ctx Saver
Powered by Claude · Grounded in docs
Security checks
- ✓README presentThe repository ships documentation.
5 checks have not been run yet.
README
ctx-saver
English | ภาษาไทย
A self-hosted MCP server (Go) that reduces AI context window usage by sandboxing large tool outputs and returning compact summaries instead of raw text.
Why
Large-output commands burn through your context window fast:
- Infrastructure:
kubectl get pods -A,docker ps -a --no-trunc,aws s3 ls --recursive - Logs & monitoring:
journalctl,docker logs,npm install(build logs),git log --all --oneline - Search:
find / -name "*.ts",grep -r pattern,curl https://api.example.com/users - Package mgmt:
pip list,go mod graph,npm ls --all - Data:
cat large_file.json,jira issue list,ls -la /var/log/
ctx-saver intercepts these, stores outputs in local SQLite with FTS5 indexing, and returns only a head+tail summary. When you need more, use ctx_search or ctx_get_full.
No cloud. No telemetry. No account. 100% auditable code.
Quick start (5 minutes)
Option A — go install (requires Go 1.25+)
go install github.com/ChonlakanSutthimatmongkhol/ctx-saver/cmd/ctx-saver@latest
The binary lands in $(go env GOPATH)/bin/. Make sure that directory is in your PATH:
# Add to ~/.zshrc or ~/.bashrc (one-time setup)
export PATH="$PATH:$(go env GOPATH)/bin"
Option B — clone and build
git clone https://github.com/ChonlakanSutthimatmongkhol/ctx-saver.git
cd ctx-saver
make install # build + copy to /usr/local/bin/ctx-saver
Configure your AI client
Claude Code
claude mcp add ctx-saver -- $(go env GOPATH)/bin/ctx-saver
VS Code Copilot — create .vscode/mcp.json in any project root:
{
"servers": {
"ctx-saver": {
"command": "/usr/local/bin/ctx-saver"
}
}
}
Option A users (
go install): replace the path above with$(go env GOPATH)/bin/ctx-saver. Or runctx-saver init copilot— it detects the binary path automatically (nojqrequired).
Or globally in VS Code settings.json:
{
"mcp.servers": {
"ctx-saver": {
"command": "/Users/<you>/go/bin/ctx-saver"
}
}
}
Verify: Command Palette → MCP: List Servers — should show ctx-saver with 11 tools.
Codex CLI — run from any project root:
ctx-saver init codex
This writes ~/.codex/config.toml (MCP server entry) and ~/.codex/hooks.json (PreToolUse, PostToolUse, SessionStart) automatically.
Install hooks and instruction files
Hooks enable automatic routing of large-output commands and session history restoration.
# Claude Code — hooks + CLAUDE.md knowledge reference
ctx-saver init claude
# VS Code Copilot — MCP server entry in .vscode/mcp.json
ctx-saver init copilot
# Codex CLI — MCP server entry + hooks in ~/.codex/
ctx-saver init codex
Add instruction files to teach the AI to use ctx-saver tools:
# Copilot Enterprise (.github/copilot-instructions.md)
ctx-saver init copilot-instructions
# Codex CLI (AGENTS.md at project root)
ctx-saver init agents-md
All commands detect the binary path automatically, back up existing config, and merge safely without overwriting unrelated settings. No jq required.
Repo clone users:
./scripts/install-hooks.sh claudeand./scripts/install-hooks.sh copilotalso work (requirejq).
For VS Code Copilot, .vscode/mcp.json currently accepts servers but rejects a top-level hooks key.
In short:
- Copilot can use
ctx-saverMCP tools normally - Copilot does not run lifecycle hooks automatically (
pretooluse,posttooluse,sessionstart) - Automatic hooks are currently supported via Claude Code only (
~/.claude/settings.json)
Note: You can still run ctx-saver hook <event> manually for one-off testing.
To remove Claude hooks:
./scripts/uninstall-hooks.sh claude
To remove VS Code Copilot server entry, delete servers.ctx-saver from .vscode/mcp.json.
See Hook behaviour below for what each hook does.
Tools
| Tool | Purpose |
|---|---|
ctx_session_init ⭐ |
Call first in every new session. Returns project rules, recent events, cached output inventory, and config. Claude Code and Codex CLI use the SessionStart hook automatically; Copilot Enterprise must call this explicitly. |
ctx_execute |
Run shell/python/go/node; large output stored + summarised (format-aware). Shows duplicate_hint if the same command ran within the last 30 min. |
ctx_read_file |
Read a file, optionally piped through a processing script. Use fields="signatures" to return only function/type/const declarations with original line numbers (Go, Python, Dart). |
ctx_outline |
Extract headings / table-of-contents from a stored output. Includes freshness field. |
ctx_get_section |
Extract a specific section by heading text (use after ctx_outline to navigate long docs). Includes freshness + user_confirmation_required fields. |
ctx_search |
FTS5 full-text search across stored outputs (supports context_lines). Per-match freshness field. Special characters auto-escaped; LIKE fallback on parse errors. |
ctx_get_full |
Retrieve complete output or a specific line range. Includes freshness + user_confirmation_required fields. Set accept_stale: true to bypass confirmation gate. |
ctx_stats |
Report storage, hook statistics, and adherence score (scope: session|today|7d|all). Use view="outputs" to list all cached outputs (replaces ctx_list_outputs). |
ctx_note |
Save or list architectural decisions that survive /compact. Use action="list" to query saved notes (replaces ctx_list_notes). |
ctx_purge |
[DESTRUCTIVE] Delete all cached outputs and session events for this project. Requires confirm="yes". Decision notes are preserved by default; pass all=true to delete them too. |
Cache Purge (v0.6.0)
Use ctx_purge to clear cached data when switching feature context, the cache is noisy/stale, or before a demo handover.
ctx_purge(confirm="yes") # deletes outputs + events; keeps notes
ctx_purge(confirm="yes", all=true) # also deletes ctx_note entries
| Target | Default | all=true |
|---|---|---|
| Cached outputs | ✅ deleted | ✅ deleted |
| Session events | ✅ deleted | ✅ deleted |
| Decision notes | ❌ kept | ✅ deleted |
⚠️ Irreversible. Deleted outputs cannot be recovered.
Decision Log (v0.5.1)
Use ctx_note to record any non-obvious design choice, discovered constraint, or confirmed tradeoff so future sessions (and post-/compact context) can recover the reasoning.
ctx_note(
text="Chose WithFreshness builder pattern; positional arg would break 15 test sites",
tags=["arch", "phase7"],
importance="high"
)
ctx_note(action="list", scope="session")
ctx_note(action="list", tags=["arch"], min_importance="high")
Decisions are scoped per-project, persist across sessions, and are automatically injected into ctx_session_init (up to 10 most recent normal+high importance items from the last 7 days).
Cache Freshness Policy (v0.5.0)
Every retrieval response includes a freshness object:
{
"freshness": {
"source_kind": "shell:kubectl",
"cached_at": "2026-04-26T03:00:00Z",
"age_seconds": 7200,
"age_human": "2h ago",
"stale_level": "aging",
"refresh_hint": ""
}
}
stale_level |
Age | AI behaviour |
|---|---|---|
fresh |
< 1 h | Use data as-is |
aging |
1–24 h | Use data as-is; note age if relevant |
stale |
1–7 days | Warn user; offer to refresh via ctx_execute |
critical |
> 7 days | Do not use for decisions. user_confirmation_required: true is set — the AI must surface the prompt to the user and wait for approval |
Auto-refresh: sources with auto_refresh: true (e.g. shell:kubectl, shell:acli) are silently re-run on retrieval when stale. The original output_id is preserved.
Bypass: pass accept_stale: true in any retrieval input to skip the confirmation gate.
Disable entirely: set freshness.enabled: false in config.
See docs/migration-v0.5.md for the full upgrade guide and configs/freshness-examples/ for sample configurations.
Smart Summarizer
ctx_execute automatically detects the output format and produces a compact, structured summary:
| Format | Detected when | Summary includes |
|---|---|---|
flutter_test |
command contains flutter test, or output has All tests passed! / Some tests failed. |
pass/fail/skip counts, failed test names, duration |
go_test |
command contains go test, or output has === RUN + --- PASS/FAIL |
package pass/fail counts, failed test details, coverage % |
json |
output is valid JSON starting with { or [ |
top-level keys + types, array length, sample value |
git_log |
command contains git log, or output starts with commit <hash> |
commit count, newest/oldest commits, top authors |
generic |
fallback for everything else | head + tail lines with omitted-line count |
Set summary.smart_format: false in config to always use the generic summariser.
Search features
Auto-escape — special characters (#, -, |, :, *, (, )) in ctx_search queries are automatically wrapped as FTS5 phrase literals. You never need to escape them manually.
LIKE fallback — if FTS5 still fails (malformed input), the search retries with a LIKE scan automatically. The response includes search_mode: "fts5" or search_mode: "like_fallback".
Synonym expansion — queries are automatically expanded using a built-in dictionary. api_path expands to [api_path, endpoint, route, url, path]; authentication expands to [auth, login, jwt, oauth, bearer, token]; etc. The response includes expanded_queries so you can see exactly what was searched.
To add project-specific synonyms, create .ctx-saver-synonyms.yaml in your project root:
payment_flow: [checkout, billing, invoice, transaction]
user_model: [account, profile, member]
Project overrides replace (not merge) any built-in entry with the same key.
How it works
Claude Code / VS Code Copilot
│
▼ MCP (stdio)
ctx-saver server (Go binary)
│
├── ctx_execute: subprocess → capture output
│ ├── small (≤5KB) → return directly
│ └── large (>5KB) → store in SQLite + return summary
│
└── SQLite (~/.local/share/ctx-saver/<hash>.db)
├── outputs table (full text, metadata)
└── outputs_fts (FTS5 + BM25 ranking)
Real usage: token reduction
Measured from real commands executed through ctx_execute in this repository.
Benchmark snapshot (2026-05-06). Assumption for token estimate: 1 token ≈ 4 bytes.
| Command | Format | Raw (bytes) | Summary (bytes) | Saving | Tokens saved |
|---|---|---|---|---|---|
go test -race -v ./... (790 lines) |
go_test ✦ |
39,313 | 115 | 99.7% | ~9,800 |
git log --oneline -500 |
git_log ✦ |
8,701 | 155 | 98.2% | ~2,135 |
| jira 200 issues (JSON) | json ✦ |
87,938 | 320 | 99.6% | ~21,905 |
kubectl get pods (100 pods) |
generic |
6,153 | 1,850 | 69.9% | ~1,076 |
app.log (2,000 lines) |
generic |
176,998 | 1,500 | 99.2% | ~43,875 |
git diff HEAD~5 (398 lines) |
generic |
21,795 | 750 | 96.6% | ~5,261 |
grep -rn 'func' ./... (500 matches) |
generic |
50,344 | 1,450 | 97.1% | ~12,224 |
| Total | 391,242 | 6,140 | 98.4% | ~96,276 |
✦ Smart-format summariser: extracts structured metadata instead of head/tail truncation.
Overall reduction in this run: 98.4% (from 391,242 bytes to 6,140 bytes).
For a dry-run comparison (head-20 + tail-5 simulation), run scripts/benchmark.sh.
Configuration
Default config lives at ~/.config/ctx-saver/config.yaml. Per-project overrides go in .ctx-saver.yaml at the project root.
sandbox:
timeout_seconds: 60
storage:
data_dir: ~/.local/share/ctx-saver
retention_days: 14
max_output_size_mb: 50
summary:
head_lines: 20
tail_lines: 5
auto_index_threshold_bytes: 32768 # 32 KB (v0.6.0+)
smart_format: true # format-aware summariser (flutter_test | go_test | json | git_log | generic)
enabled_formatters: [] # empty = all enabled; list names to restrict
dedup:
enabled: true
window_minutes: 30 # show duplicate_hint if same command ran within this window
logging:
level: info
file: ~/.local/share/ctx-saver/server.log
hooks:
session_history_limit: 10 # max events injected into SessionStart context
deny_commands:
- "rm -rf /"
- "sudo *"
- "dd if=*"
freshness:
enabled: true
default_max_age_seconds: 3600 # 1 hour for unknown sources
user_confirm_threshold_seconds: 604800 # 7 days → ask user before use
sources:
shell:kubectl: { max_age_seconds: 60, auto_refresh: true }
shell:acli: { max_age_seconds: 300, auto_refresh: true }
shell:git: { max_age_seconds: 120, auto_refresh: false }
# see configs/freshness-examples/ for more presets
Token efficiency tuning
The auto_index_threshold_bytes setting controls when outputs are returned inline
vs stored with an output_id:
| Value | Effect |
|---|---|
32768 (default) |
Typical Go/Python source files (300–500 lines) return inline — no round-trip needed |
65536 |
Keeps medium build outputs inline; uses more per-turn tokens but fewer tool calls |
5120 |
Legacy v0.5.x behavior — forces output_id flow for most files |
Configure in ~/.config/ctx-saver/config.yaml:
summary:
auto_index_threshold_bytes: 32768 # adjust to taste
Hooks
Hooks run as lightweight subprocesses alongside the AI agent. They share the same binary (ctx-saver hook <event>) so no extra installation is needed after make install.
| Hook | Event | What it does |
|---|---|---|
| PreToolUse | Before any shell/bash tool call | Blocks dangerous commands (rm -rf, pipe-to-shell, eval, sudo -s); redirects large-output commands (curl, wget, cat *.log, find, journalctl) to equivalent ctx_execute calls |
| PostToolUse | After every tool call | Records a summary of the tool call to the per-project SQLite DB for session restoration |
| SessionStart | At the start of every session | Injects routing rules and recent session history (up to hooks.session_history_limit deduplicated events) into the model's context |
Safe curl variants
curl --version, curl -I, curl --head, and curl -o /dev/null are not redirected — only requests that are likely to return large bodies are sent through ctx_execute.
Dangerous command patterns blocked by PreToolUse
rm -rf/rm -fr/ anyrm -[rRfF]+variantfind / … -delete- Redirect to raw disk (
> /dev/sda,> /dev/nvme0) - Pipe to shell interpreter (
curl … | bash,wget … | sh,| zsh, …) - Any form of
eval sudo -s,sudo rm,sudo dd- Reads of credential files (
.env,id_rsa,.pem,.key)
For Copilot Enterprise users
If you are using GitHub Copilot in an enterprise context (e.g., at a bank or fintech), see the Copilot Enterprise Setup Guide for:
- Required admin policies (MCP server allowlist)
- Installation steps specific to VS Code Copilot Agent mode
- Verification procedure (
ctx_session_initcall) - Troubleshooting tool-adherence issues
- How to engage IT/Security for MCP approval
Quick start:
# 1. Install ctx-saver binary
go install github.com/ChonlakanSutthimatmongkhol/ctx-saver/cmd/ctx-saver@latest
# 2. Add MCP server to VS Code (run from your project directory)
ctx-saver init copilot
# 3. Add Copilot instruction rules to your repo
ctx-saver init copilot-instructions
For Codex CLI users
# 1. Install ctx-saver binary
go install github.com/ChonlakanSutthimatmongkhol/ctx-saver/cmd/ctx-saver@latest
# 2. Install MCP server + hooks into ~/.codex/
ctx-saver init codex
# 3. Add Codex instruction rules to your repo
ctx-saver init agents-md
ctx-saver init codex writes:
~/.codex/config.toml— MCP server entry (idempotent; safe to run twice)~/.codex/hooks.json— PreToolUse, PostToolUse, SessionStart hooks
ctx-saver init agents-md creates AGENTS.md at the project root. Commit it to share ctx-saver routing rules with your team.
Restart Codex CLI after running init codex to activate the hooks.
Project Knowledge
The longer you use ctx-saver with a project, the more it learns about it.
After 3+ sessions, ctx-saver can generate a .ctx-saver/project-knowledge.md
file containing:
- Most-read files — with cache stability (hash unchanged or frequently changing)
- Most-run commands — with average output size
- Common sequences — command pairs that tend to run together
- High-importance decisions — notes tagged with
importance=high - Session patterns — session and output counts
This file is referenced from CLAUDE.md / copilot-instructions.md so every
AI session starts with project context at zero extra token cost per turn.
Generating knowledge
ctx-saver knowledge refresh # generate/update project-knowledge.md
ctx-saver knowledge show # print to stdout (no file write)
ctx-saver knowledge reset # delete project-knowledge.md
ctx-saver knowledge refresh --quiet # suppress output (for cron)
Cron setup (recommended)
# macOS/Linux — refresh every weeknight at 23:00
crontab -e
# Add:
0 23 * * 1-5 cd /path/to/project && ctx-saver knowledge refresh --quiet
# View server log
tail -f ~/.local/share/ctx-saver/server.log
Idle detection runs automatically while the MCP server is active: after
knowledge.idle_minutes (default 30) of inactivity, knowledge is refreshed
in the background — no cron needed during active sessions.
Configuration
knowledge:
min_sessions: 3 # sessions required before first generation
idle_minutes: 30 # 0 = disable idle detection
top_files_limit: 10
top_commands_limit: 10
decisions_limit: 10
Build
# Requires Go 1.25+
make build # → bin/ctx-saver
make test # unit tests
make lint # golangci-lint
make install # → /usr/local/bin/ctx-saver
Security
- SQLite database permissions:
0600(owner read/write only) - Command deny list checked before execution
- Binary output (null bytes) rejected
- Path traversal resolved via
filepath.Abs+filepath.Clean - Log file truncates command strings to 120 chars to avoid logging secrets
- No network access — purely local
Repository structure
cmd/ctx-saver/main.go entry point
internal/config/ YAML config loader
internal/sandbox/ execution interface (subprocess + srt stub)
internal/store/ SQLite + FTS5 storage layer
internal/summary/ smart summariser: format-aware (flutter_test, go_test, json, git_log) + generic fallback
internal/summary/formats/ one file per formatter + tests
internal/search/ synonym expansion (builtin YAML + project override)
internal/handlers/ one file per MCP tool
internal/hooks/ PreToolUse / PostToolUse / SessionStart hooks
internal/server/ MCP server wiring
tests/ integration tests + testdata
scripts/ install.sh, install-hooks.sh, uninstall-hooks.sh, benchmark.sh
configs/ setup guides and hook config templates per platform
Design decisions
Subprocess sandbox (not containers or VMs)
Why: Simplicity and instant startup. Containers add 50–200ms overhead per execution; subprocess spawns in ~1ms. For tools that run in your session anyway (like AI chat), sandboxing via exec.Command with resource limits is sufficient. We focus on isolation of outputs, not processes — the threat model is context pollution, not malware.
Future: Anthropic's srt (Secure Runtime) can be added for enforced OS-level isolation when needed (toggle via sandbox.use_srt: true).
FTS5 over traditional indexing
Why: BM25 ranking in FTS5 is built-in and tuned for natural language search. Matching "pod status" across 50MB of kubectl logs returns relevant lines first, not just substring matches. No extra query complexity — just SELECT … FROM outputs_fts WHERE outputs_fts MATCH 'pod status'.
SQLite instead of Redis/Postgres
Why: Self-hosted. No external services to manage. sqlite lives in a single ~/.local/share/ctx-saver/<hash>.db file — permissions are 0600, backups are trivial, and you own your data. For a tool that runs locally on your machine, zero-config beats "set up a database server."
Per-project database hash
Why: Isolation. Your ~/projects/backend database is separate from ~/projects/frontend. Tools and configs are independent per project, but stats can still be queried across all projects (scope: all).
Head+tail summaries instead of full-text extraction
Why: Bounded context. Taking the first 20 lines + last 5 lines of a 1000-line JSON response guarantees ~100 tokens instead of ~3000. You see structure and key info instantly, can search for details if needed, and spend context on what matters.
README copy captured when this server was indexed; it may lag the repository. The original is authoritative.